Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljscodegenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5#include "qqmljsmetatypes_p.h"
7#include "qqmljsscope_p.h"
8#include "qqmljsutils_p.h"
9
10#include <private/qqmljstypepropagator_p.h>
11
12#include <private/qqmlirbuilder_p.h>
13#include <private/qqmljsscope_p.h>
14#include <private/qqmljsutils_p.h>
15#include <private/qv4compilerscanfunctions_p.h>
16#include <private/qduplicatetracker_p.h>
17
18#include <QtCore/qdir.h>
19#include <QtCore/qfileinfo.h>
20
22
23using namespace Qt::StringLiterals;
24
34#define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
35
36#define INJECT_TRACE_INFO(function) \
37 static const bool injectTraceInfo = true; \
38 if (injectTraceInfo) { \
39 m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \
40 }
41
42
43static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
44{
45 return !type.isNull()
46 && !resolver->equals(type, resolver->nullType())
47 && !resolver->equals(type, resolver->voidType());
48}
49
50QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const
51{
52 return type->augmentedInternalName();
53}
54
56 const QV4::Compiler::JSUnitGenerator *unitGenerator,
57 const QQmlJSTypeResolver *typeResolver,
58 QQmlJSLogger *logger, BasicBlocks basicBlocks,
59 InstructionAnnotations annotations)
60 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
61 , m_context(compilerContext)
62{}
63
65{
66 return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
67}
68
70{
71 return u"[]() { static const auto t = QMetaType::fromName(\""_s
72 + QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
73 + u"\"); return t; }()"_s;
74}
75
77{
78 return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeListMetaType("
79 "aotContext->compilationUnit, QStringLiteral(\""_s
80 + elementName
81 + u"\")); return t; }(aotContext)"_s;
82}
83
85{
86 return u"[](auto *aotContext) { static const auto t = QQmlPrivate::compositeMetaType("
87 "aotContext->compilationUnit, QStringLiteral(\""_s
88 + elementName
89 + u"\")); return t; }(aotContext)"_s;
90}
91
93{
94 if (objectType->isComposite()) {
95 const QString name = m_typeResolver->nameForType(objectType);
96 if (name.isEmpty()) {
97 reject(u"retrieving the metaObject of a composite type without an element name."_s);
98 return QString();
99 }
100 return compositeMetaType(name) + u".metaObject()"_s;
101 }
102
103 if (objectType->internalName() == u"QObject"_s
104 || objectType->internalName() == u"QQmlComponent"_s) {
105 return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
106 }
107 return metaTypeFromName(objectType) + u".metaObject()"_s;
108}
109
111{
112 if (type->isComposite()) {
114 if (!name.isEmpty())
115 return compositeMetaType(name);
116 }
117
118 if (type->isListProperty() && type->valueType()->isComposite()) {
119 const QString name = m_typeResolver->nameForType(type->valueType());
120 if (!name.isEmpty())
122 }
123
127}
128
131 bool basicBlocksValidationFailed)
132{
134 m_error = error;
135
136 QHash<int, int> numRegisterVariablesPerIndex;
137
138 const auto addVariable
139 = [&](int registerIndex, int lookupIndex, const QQmlJSScope::ConstPtr &seenType) {
140 // Don't generate any variables for registers that are initialized with undefined.
141 if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
142 return;
143
144 const RegisterVariablesKey key = { seenType->internalName(), registerIndex, lookupIndex };
145
146
147 const auto oldSize = m_registerVariables.size();
148 auto &e = m_registerVariables[key];
149 if (m_registerVariables.size() != oldSize) {
150 e.variableName = u"r%1_%2"_s
151 .arg(registerIndex)
152 .arg(numRegisterVariablesPerIndex[registerIndex]++);
153 e.storedType = m_typeResolver->comparableType(seenType);
154 }
155 ++e.numTracked;
156 };
157
159QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
160 for (const auto &annotation : m_annotations) {
161 addVariable(annotation.second.changedRegisterIndex,
162 annotation.second.changedRegister.resultLookupIndex(),
163 annotation.second.changedRegister.storedType());
164 for (auto it = annotation.second.typeConversions.begin(),
165 end = annotation.second.typeConversions.end();
166 it != end; ++it) {
167 addVariable(
168 it.key(), it.value().content.resultLookupIndex(),
169 it.value().content.storedType());
170 }
171 }
173
174 // ensure we have m_labels for loops
175 for (const auto loopLabel : m_context->labelInfo)
176 m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
177
178 // Initialize the first instruction's state to hold the arguments.
179 // After this, the arguments (or whatever becomes of them) are carried
180 // over into any further basic blocks automatically.
181 m_state.State::operator=(initialState(m_function));
182
183 const QByteArray byteCode = function->code;
184 decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
185
187 result.includes.swap(m_includes);
188
189 if (basicBlocksValidationFailed) {
190 result.code += "// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
191 "with basic blocks validation but compiled without it.\n"_L1;
192 }
193
194 result.code += u"// %1 at line %2, column %3\n"_s
195 .arg(m_context->name).arg(m_context->line).arg(m_context->column);
196
197 for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
198 registerIt != registerEnd; ++registerIt) {
199
200 const int registerIndex = registerIt.key().registerIndex;
201 const bool registerIsArgument = isArgument(registerIndex);
202
203 result.code += registerIt.key().internalName;
204
205 const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
206 const bool isPointer
207 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
208 if (isPointer)
209 result.code += u" *"_s;
210 else
211 result.code += u' ';
212
213 if (!registerIsArgument
214 && registerIndex != Accumulator
215 && registerIndex != This
217 function->registerTypes[registerIndex - firstRegisterIndex()],
219 result.code += registerIt->variableName + u" = "_s;
220 result.code += convertStored(m_typeResolver->voidType(), storedType, QString());
221 } else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
222 argumentType(registerIndex), storedType)) {
223 const int argumentIndex = registerIndex - FirstArgument;
225 = m_function->argumentTypes[argumentIndex];
226 const QQmlJSRegisterContent original
228
229 const bool needsConversion = argument != original;
230 if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
231 // Not a pointer, never written to, and doesn't need any initial conversion.
232 // This is a readonly argument.
233 //
234 // We would like to make the variable a const ref if it's a readonly argument,
235 // but due to the various call interfaces accepting non-const values, we can't.
236 // We rely on those calls to still not modify their arguments in place.
237 result.code += u'&';
238 }
239
240 result.code += registerIt->variableName + u" = "_s;
241
243 = u"(*static_cast<"_s + castTargetName(original.storedType())
244 + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
245
246 if (needsConversion)
247 result.code += conversion(original, argument, originalValue);
248 else
249 result.code += originalValue;
250 } else {
251 result.code += registerIt->variableName;
252 }
253 result.code += u";\n"_s;
254 }
255
256 result.code += m_body;
257
258
259 QString signature
260 = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { unit };\n"
261 " const auto *aotContext = &c;\n"
262 " Q_UNUSED(aotContext);\n"_s;
263
264 if (function->returnType.isValid()) {
265 signature += u" argTypes[0] = %1;\n"_s.arg(
266 metaType(m_typeResolver->containedType(function->returnType)));
267 } else {
268 signature += u" argTypes[0] = QMetaType();\n"_s;
269 }
270 result.numArguments = function->argumentTypes.length();
271 for (qsizetype i = 0; i != result.numArguments; ++i) {
272 signature += u" argTypes[%1] = %2;\n"_s.arg(
273 QString::number(i + 1),
274 metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
275 }
276
277 result.signature = signature;
278 return result;
279}
280
282{
283 const auto finalizeReturn = qScopeGuard([this]() { m_body += u"return;\n"_s; });
284
285 m_body += u"aotContext->setReturnValueUndefined();\n"_s;
286 const auto ret = m_function->returnType;
288 return;
289
290 m_body += u"if (argv[0]) {\n"_s;
291
292 const auto contained = m_typeResolver->containedType(ret);
293 const auto stored = ret.storedType();
294 if (contained->isReferenceType() && stored->isReferenceType()) {
295 m_body += u" *static_cast<"_s
296 + stored->augmentedInternalName()
297 + u" *>(argv[0]) = nullptr;\n"_s;
298 } else if (m_typeResolver->equals(contained, stored)) {
299 m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
300 + stored->internalName() + u"();\n"_s;
301 } else {
302 m_body += u" const QMetaType returnType = "_s
304 m_body += u" returnType.destruct(argv[0]);\n"_s;
305 m_body += u" returnType.construct(argv[0]);\n "_s;
306 }
307
308 m_body += u"}\n"_s;
309}
310
312{
314
315 const auto finalizeReturn = qScopeGuard([this]() {
316 m_body += u"return;\n"_s;
317 m_skipUntilNextLabel = true;
318 resetState();
319 });
320
322 return;
323
324 m_body += u"if (argv[0]) {\n"_s;
325
326 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
328
329 if (in.isEmpty()) {
332 m_body += signalUndefined;
333
334 }
337 m_body += u" if (!"_s + in + u".isValid())\n"_s;
338 m_body += u" "_s + signalUndefined;
341 m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
342 m_body += u" "_s + signalUndefined;
345 m_body += u" if ("_s + in + u".isUndefined())\n"_s;
346 m_body += u" "_s + signalUndefined;
347 }
348
351 m_body += u"}\n"_s;
352 return;
353 }
354
355 const auto contained = m_typeResolver->containedType(m_function->returnType);
356 const auto stored = m_function->returnType.storedType();
357 if (m_typeResolver->equals(contained, stored)
358 || (contained->isReferenceType() && stored->isReferenceType())) {
359 m_body += u" *static_cast<"_s
360 + stored->augmentedInternalName()
361 + u" *>(argv[0]) = "_s
364 + u";\n"_s;
365 } else if (m_typeResolver->registerContains(m_state.accumulatorIn(), contained)) {
366 m_body += u" const QMetaType returnType = "_s + contentType(m_state.accumulatorIn(), in)
367 + u";\n"_s;
368 m_body += u" returnType.destruct(argv[0]);\n"_s;
369 m_body += u" returnType.construct(argv[0], "_s
370 + contentPointer(m_state.accumulatorIn(), in) + u");\n"_s;
371 } else {
372 m_body += u" const auto converted = "_s
374 consumedAccumulatorVariableIn()) + u";\n"_s;
375 m_body += u" const QMetaType returnType = "_s
376 + contentType(m_function->returnType, u"converted"_s)
377 + u";\n"_s;
378 m_body += u" returnType.destruct(argv[0]);\n"_s;
379 m_body += u" returnType.construct(argv[0], "_s
380 + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
381 }
382
383 m_body += u"}\n"_s;
384}
385
390
392{
393 if (value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max()) {
394 const int i = value;
395 if (i == value)
396 return QString::number(i);
397 }
398
399 switch (qFpClassify(value)) {
400 case FP_INFINITE: {
401 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
402 return std::signbit(value) ? (u'-' + inf) : inf;
403 }
404 case FP_NAN:
405 return u"std::numeric_limits<double>::quiet_NaN()"_s;
406 case FP_ZERO:
407 return std::signbit(value) ? u"-0.0"_s : u"0"_s;
408 default:
409 break;
410 }
411
412 return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
413}
414
416{
418
419 // You cannot actually get it to generate LoadConst for anything but double. We have
420 // a numer of specialized instructions for the other types, after all. However, let's
421 // play it safe.
422
423 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
426
428 if (type == m_typeResolver->realType()) {
431 toNumericString(value.doubleValue()));
432 } else if (type == m_typeResolver->int32Type()) {
435 QString::number(value.integerValue()));
436 } else if (type == m_typeResolver->boolType()) {
439 value.booleanValue() ? u"true"_s : u"false"_s);
440 } else if (type == m_typeResolver->voidType()) {
443 QString());
444 } else if (type == m_typeResolver->nullType()) {
447 u"nullptr"_s);
448 } else {
449 reject(u"unsupported constant type"_s);
450 }
451
452 m_body += u";\n"_s;
453}
454
464
474
484
494
504
515
516void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
517{
519
521
523 if (var.isEmpty())
524 return; // Do not load 'undefined'
525
526 const auto v4Value = QV4::StaticValue::fromReturnedValue(
527 m_jsUnitGenerator->constant(constIndex));
528
529 const auto changed = m_state.changedRegister();
530 QQmlJSScope::ConstPtr contained;
532
533 m_body += var + u" = "_s;
534 if (v4Value.isNull()) {
535 contained = m_typeResolver->nullType();
536 } else if (v4Value.isUndefined()) {
537 contained = m_typeResolver->voidType();
538 } else if (v4Value.isBoolean()) {
539 contained = m_typeResolver->boolType();
540 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
541 } else if (v4Value.isInteger()) {
542 contained = m_typeResolver->int32Type();
543 input = QString::number(v4Value.int_32());
544 } else if (v4Value.isDouble()) {
545 contained = m_typeResolver->realType();
546 input = toNumericString(v4Value.doubleValue());
547 } else {
548 reject(u"unknown const type"_s);
549 return;
550 }
551 m_body += conversion(contained, changed, input) + u";\n"_s;
552}
553
564
566{
568
572 if (var.isEmpty())
573 return; // don't store "undefined"
574 m_body += var;
575 m_body += u" = "_s;
578 m_body += u";\n"_s;
579}
580
581void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
582{
584
586 const QString destRegName = changedRegisterVariable();
587 if (destRegName.isEmpty())
588 return; // don't store things we cannot store.
589 m_body += destRegName;
590 m_body += u" = "_s;
593 m_body += u";\n"_s;
594}
595
601
603{
605 reject(u"LoadLocal"_s);
606}
607
613
620
627
638
639void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
640{
641 Q_UNUSED(regExpId)
642 Q_UNUSED(destReg)
644}
645
647{
649 reject(u"LoadClosure"_s);
650}
651
653{
654 Q_UNUSED(nameIndex)
655 reject(u"LoadName"_s);
656}
657
659{
661
662 AccumulatorConverter registers(this);
663
664 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index)
665 + u", &"_s + m_state.accumulatorVariableOut + u", "_s
667 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
668 + QString::number(index) + u')';
669 generateLookup(lookup, initialization);
670}
671
673{
675
676 AccumulatorConverter registers(this);
677
678 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
679 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
682 + conversion(
684 u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
685 + u";\n"_s;
686 return;
687 }
688
689 const QString indexString = QString::number(index);
691 const QString lookup = u"aotContext->loadContextIdLookup("_s
692 + indexString + u", "_s
694 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
695 + indexString + u')';
696 generateLookup(lookup, initialization);
697 return;
698 }
699
700 const bool isProperty = m_state.accumulatorOut().isProperty();
703 if (isProperty) {
705
706 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
707 + indexString + u", "_s
709 const QString initialization
710 = u"aotContext->initLoadScopeObjectPropertyLookup("_s
711 + indexString + u", "_s
712 + lookupType + u')';
713 const QString preparation = getLookupPreparation(
715
716 generateLookup(lookup, initialization, preparation);
718 generateTypeLookup(index);
719 } else {
720 reject(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
721 }
722}
723
725{
727
728 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
730 Q_ASSERT(type.isProperty());
731
732 switch (type.variant()) {
735 // Do not convert here. We may intentionally pass the "wrong" type, for example to trigger
736 // a property reset.
737 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
738 + u", "_s
740 + u", "_s
742 m_body += u";\n"_s;
743 break;
744 }
747 reject(u"assignment to scope method"_s);
748 break;
749 default:
750 Q_UNREACHABLE();
751 }
752}
753
759
761{
763
764 const QQmlJSRegisterContent baseType = registerType(base);
765
766 if (!baseType.isList()
768 reject(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
769 return;
770 }
771
772 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
774 m_state.accumulatorOut(), QString()) + u";\n"_s;
775
777 QQmlJSScope::ConstPtr indexType;
780 } else if (m_state.accumulatorIn().isConversion()) {
783 indexType = target;
784 m_body += u"if (!" + indexName + u".metaType().isValid())\n"
785 + voidAssignment
786 + u"else ";
787 indexName = convertStored(
788 m_state.accumulatorIn().storedType(), indexType, indexName);
789 } else {
790 reject(u"LoadElement with non-numeric argument"_s);
791 return;
792 }
793 }
794
795 AccumulatorConverter registers(this);
796 const QString baseName = registerVariable(base);
797
798 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
799 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
800 + voidAssignment
801 + u"else "_s;
802 } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
803 m_body += u"if ("_s + indexName + u" < 0)\n"_s
804 + voidAssignment
805 + u"else "_s;
806 }
807
809 // Our QQmlListProperty only keeps plain QObject*.
810 const auto elementType = m_typeResolver->globalType(m_typeResolver->qObjectType());
811
812 m_body += u"if ("_s + indexName + u" < "_s + baseName
813 + u".count(&"_s + baseName + u"))\n"_s;
814 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
815 conversion(elementType, m_state.accumulatorOut(),
816 baseName + u".at(&"_s + baseName + u", "_s
817 + indexName + u')') + u";\n"_s;
818 m_body += u"else\n"_s
819 + voidAssignment;
820 return;
821 }
822
823 const auto elementType = m_typeResolver->valueType(baseType);
824
825 QString access = baseName + u".at("_s + indexName + u')';
826
827 // TODO: Once we get a char type in QML, use it here.
829 access = u"QString("_s + access + u")"_s;
831 reject(u"LoadElement on a sequence potentially affected by side effects"_s);
832 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
833 reject(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
834
835 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
836 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
837 conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
838 m_body += u"else\n"_s
839 + voidAssignment;
840}
841
843{
845
846 const QQmlJSRegisterContent baseType = registerType(base);
848
849 if (!m_typeResolver->isNumeric(indexType) || !baseType.isList()) {
850 reject(u"StoreElement with non-list base type or non-numeric arguments"_s);
851 return;
852 }
853
854 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
855 reject(u"indirect StoreElement"_s);
856 return;
857 }
858
859 const QString baseName = registerVariable(base);
860 const QString indexName = registerVariable(index);
861
862 const auto valueType = m_typeResolver->valueType(baseType);
863 const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType(
864 m_typeResolver->containedType(valueType)));
865
866 addInclude(u"QtQml/qjslist.h"_s);
867 if (!m_typeResolver->isNativeArrayIndex(indexType))
868 m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
869 else if (!m_typeResolver->isUnsignedInteger(indexType))
870 m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
871 else
872 m_body += u"{\n"_s;
873
875 m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
876 + u"))\n"_s;
877 m_body += u" "_s + baseName + u".replace(&"_s + baseName
878 + u", "_s + indexName + u", "_s;
880 + u");\n"_s;
881 m_body += u"}\n"_s;
882 return;
883 }
884
886 reject(u"LoadElement on a sequence potentially affected by side effects"_s);
887
888 m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
889 m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
890 + indexName + u" + 1);\n"_s;
891 m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
893 + u";\n"_s;
894 m_body += u"}\n"_s;
895
896 generateWriteBack(base);
897}
898
900{
901 Q_UNUSED(nameIndex)
902 reject(u"LoadProperty"_s);
903}
904
911
913{
914 const QString enumMember = m_state.accumulatorOut().enumMember();
915
916 if (enumMember.isEmpty()) {
917 // If we're referring to the type, there's nothing to do.
918 // However, we should not get here since no one can ever use the enum metatype.
919 // The lookup is dead code and should be optimized away.
920 // ... unless you are actually trying to store the metatype itself in a property.
921 // We cannot compile such code.
922 reject(u"Lookup of enum metatype"_s);
923 return;
924 }
925
926 // If the metaenum has the value, just use it and skip all the rest.
928 if (metaEnum.hasValues()) {
930 + QString::number(metaEnum.value(enumMember));
931 m_body += u";\n"_s;
932 return;
933 }
934
936
937 // Otherwise we would have found an enum with values.
938 Q_ASSERT(!scopeType->isComposite());
939
940 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
941 if (enumName.isEmpty()) {
942 if (metaEnum.isFlag() && !metaEnum.name().isEmpty())
943 reject(u"qmltypes misses name entry for flag; did you pass the enum type to Q_FLAG instead of the QFlag type?"
944 "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
945 reject(u"qmltypes misses name entry for enum"_s);
946 }
947 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
948 + u", &"_s + m_state.accumulatorVariableOut + u')';
949 const QString initialization = u"aotContext->initGetEnumLookup("_s
950 + QString::number(index) + u", "_s + metaObject(scopeType)
951 + u", \""_s + enumName + u"\", \""_s + enumMember
952 + u"\")"_s;
953 generateLookup(lookup, initialization);
954}
955
956void QQmlJSCodeGenerator::generateTypeLookup(int index)
957{
958 const QString indexString = QString::number(index);
960 const QString namespaceString
961 = accumulatorIn.isImportNamespace()
962 ? QString::number(accumulatorIn.importNamespace())
963 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
964
965 switch (m_state.accumulatorOut().variant()) {
967 rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
968 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
969 + u", &"_s + m_state.accumulatorVariableOut + u')';
970 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
971 + u", "_s + namespaceString + u')';
972 generateLookup(lookup, initialization);
973 break;
974 }
976 break;
978 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
979 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
980 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
981 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
982 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
983 generateLookup(lookup, initialization);
984 break;
985 }
987 reject(u"script lookup"_s);
988 break;
992 // TODO: Can we trigger this somehow?
993 // It might be impossible, but we better be safe here.
994 reject(u"meta-object stored in different type"_s);
995 }
996 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
997 + u", &"_s + m_state.accumulatorVariableOut + u')';
998 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
999 + u", "_s + namespaceString + u")"_s;
1000 generateLookup(lookup, initialization);
1001 break;
1002 }
1003 default:
1004 Q_UNREACHABLE();
1005 }
1006}
1007
1008void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1009 const QQmlJSRegisterContent &nonStorableContent, const QString &registerName, bool invert)
1010{
1011 const auto nonStorableType = m_typeResolver->containedType(nonStorableContent);
1012 QQmlJSScope::ConstPtr comparedType =
1013 m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())
1016
1017 // The common operations for both nulltype and voidtype
1018 m_body += u"if ("_s + registerName
1019 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
1020 + m_state.accumulatorVariableOut + u" = "_s
1022 u"static_cast<const QJSPrimitiveValue *>("_s + registerName
1023 + u".constData())"_s + u"->type() "_s
1024 + (invert ? u"!="_s : u"=="_s)
1025 + (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
1026 ? u"QJSPrimitiveValue::Null"_s
1027 : u"QJSPrimitiveValue::Undefined"_s))
1028 + u";\n} else if ("_s + registerName
1029 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
1030 + m_state.accumulatorVariableOut + u" = "_s
1031 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1032 (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
1033 + registerName + u".constData())"_s + u"->"_s
1034 + (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
1035 ? u"isNull()"_s
1036 : u"isUndefined()"_s))
1037 + u";\n}"_s;
1038
1039 // Generate nullType specific operations (the case when variant contains QObject * or
1040 // std::nullptr_t)
1041 if (m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())) {
1042 m_body += u"else if ("_s + registerName
1043 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
1044 + m_state.accumulatorVariableOut + u" = "_s
1046 u"*static_cast<QObject *const *>("_s + registerName
1047 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
1048 + u" nullptr"_s)
1049 + u";\n} else if ("_s + registerName
1050 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
1051 + m_state.accumulatorVariableOut + u" = "_s
1052 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1053 (invert ? u"false"_s : u"true"_s))
1054 + u";\n}\n"_s;
1055 }
1056
1057 // fallback case (if variant contains a different type, then it is not null or undefined)
1058 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
1060 (invert ? (registerName + u".isValid() ? true : false"_s)
1061 : (registerName + u".isValid() ? false : true"_s)))
1062 + u";\n}"_s;
1063}
1064
1065void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1066 const QQmlJSRegisterContent &storableContent, const QString &typedRegisterName,
1067 const QString &varRegisterName, bool invert)
1068{
1069 // enumerations are ===-equal to their underlying type and they are stored as such.
1070 // Therefore, use the underlying type right away.
1071 const auto contained = storableContent.isEnumeration()
1072 ? storableContent.storedType()
1073 : m_typeResolver->containedType(storableContent);
1074
1075 if (contained->isReferenceType()) {
1076 const QQmlJSRegisterContent comparable
1078 m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString()) + u"(("
1079 + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
1080 + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
1081 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n";
1082 return;
1083 }
1084
1085 if (m_typeResolver->isPrimitive(contained)) {
1086 const QQmlJSRegisterContent comparable
1088 m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString())
1089 + conversion(storableContent, comparable, typedRegisterName)
1090 + u".strictlyEquals("_s
1091 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u");\n"_s;
1092 return;
1093 }
1094
1095 reject(u"comparison of non-primitive, non-object type to var"_s);
1096}
1097
1098void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
1099{
1101 const QQmlJSScope::ConstPtr value = stored->valueType();
1102 Q_ASSERT(value);
1103
1104 QStringList initializer;
1105 for (int i = 0; i < argc; ++i) {
1106 initializer += convertStored(
1107 registerType(argv + i).storedType(), value,
1108 consumedRegisterVariable(argv + i));
1109 }
1110
1111 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
1112 m_body += initializer.join(u", "_s);
1113 m_body += u"};\n";
1114}
1115
1116void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
1117{
1118 QString writeBackRegister = registerVariable(registerIndex);
1119 bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(registerIndex);
1120
1121 for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
1122 !writeBack.storedType()->isReferenceType();) {
1123 if (writeBackAffectedBySideEffects)
1124 reject(u"write-back of value affected by side effects"_s);
1125
1126 if (writeBack.isConversion())
1127 reject(u"write-back of converted value"_s);
1128
1129 const int lookupIndex = writeBack.resultLookupIndex();
1130 if (lookupIndex == -1) {
1131 // This is essential for the soundness of the type system.
1132 //
1133 // If a value or a list is returned from a function, we cannot know
1134 // whether it is a copy or a reference. Therefore, we cannot know whether
1135 // we have to write it back and so we have to reject any write on it.
1136 //
1137 // Only if we are sure that the value is locally created we can be sure
1138 // we don't have to write it back. In this latter case we could allow
1139 // a modification that doesn't write back.
1140 reject(u"write-back of non-lookup"_s);
1141 break;
1142 }
1143
1144 const QString writeBackIndexString = QString::number(lookupIndex);
1145
1146 const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
1149 const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
1150 + writeBackIndexString
1151 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1152 const QString initialization
1153 = u"aotContext->initLoadScopeObjectPropertyLookup("_s
1154 + writeBackIndexString
1155 + u", "_s + contentType(writeBack, writeBackRegister) + u')';
1156 generateLookup(lookup, initialization);
1157 break;
1158 }
1159
1160
1161 QQmlJSRegisterContent outerContent;
1162 QString outerRegister;
1163 bool outerAffectedBySideEffects = false;
1165 it != end; ++it) {
1166 if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
1167 outerContent = it.value().content;
1168 outerRegister = lookupVariable(outerContent.resultLookupIndex());
1169 outerAffectedBySideEffects = it.value().affectedBySideEffects;
1170 break;
1171 }
1172 }
1173
1174 if (!outerContent.isValid()) {
1175 // If the lookup doesn't exist, it was killed by state merge.
1176 reject(u"write-back of lookup across jumps or merges."_s);
1177 break;
1178 }
1179
1180 Q_ASSERT(!outerRegister.isEmpty());
1181
1182 switch (writeBack.variant()) {
1185 Q_UNREACHABLE();
1188 if (writeBack.scopeType()->isReferenceType()) {
1189 const QString lookup = u"aotContext->writeBackObjectLookup("_s
1190 + writeBackIndexString
1191 + u", "_s + outerRegister
1192 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1193 const QString initialization = u"aotContext->initGetObjectLookup("_s
1194 + writeBackIndexString
1195 + u", "_s + outerRegister
1196 + u", "_s + contentType(writeBack, writeBackRegister) + u')';
1197 generateLookup(lookup, initialization);
1198 } else {
1199 const QString valuePointer = contentPointer(outerContent, outerRegister);
1200 const QString lookup = u"aotContext->writeBackValueLookup("_s
1201 + writeBackIndexString
1202 + u", "_s + valuePointer
1203 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1204 const QString initialization = u"aotContext->initGetValueLookup("_s
1205 + writeBackIndexString
1206 + u", "_s + metaObject(writeBack.scopeType())
1207 + u", "_s + contentType(writeBack, writeBackRegister) + u')';
1208 generateLookup(lookup, initialization);
1209 }
1210 break;
1211 default:
1212 reject(u"SetLookup on value types (because of missing write-back)"_s);
1213 }
1214
1215 writeBackRegister = outerRegister;
1216 writeBack = outerContent;
1217 writeBackAffectedBySideEffects = outerAffectedBySideEffects;
1218 }
1219}
1220
1221void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
1222{
1224 != QQmlJSScope::AccessSemantics::Reference) {
1225 reject(error);
1226 }
1227}
1228
1229void QQmlJSCodeGenerator::rejectIfBadArray()
1230{
1232 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
1233 // This rejects any attempt to store the list into a QVariant.
1234 // Therefore, we don't have to adjust the contained type below.
1235
1236 reject(u"storing an array in a non-sequence type"_s);
1237 } else if (stored->isListProperty()) {
1238 // We can, technically, generate code for this. But it's dangerous:
1239 //
1240 // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
1241 // m_body += stored->internalName() + u"::ListType " + storage
1242 // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
1243 // m_body += m_state.accumulatorVariableOut
1244 // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
1245
1246 reject(u"creating a QQmlListProperty not backed by a property"_s);
1247
1248 }
1249}
1250
1258bool QQmlJSCodeGenerator::generateContentPointerCheck(
1259 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1260 const QString &variable, const QString &errorMessage)
1261{
1262 const QQmlJSScope::ConstPtr scope = required;
1265 [&](const QQmlJSScope::ConstPtr &base) {
1266 return m_typeResolver->equals(base, scope);
1267 })) {
1268 return false;
1269 }
1270
1271 if (!m_typeResolver->canHold(input, scope)) {
1272 reject(u"lookup of members of %1 in %2"_s.arg(
1273 scope->internalName(), input->internalName()));
1274 }
1275
1276 bool needsVarContentConversion = false;
1277 QString processedErrorMessage;
1278 if (actual.storedType()->isReferenceType()) {
1279 // Since we have verified the type in qqmljstypepropagator.cpp we now know
1280 // that we can only have either null or the actual type here. Therefore,
1281 // it's enough to check the pointer for null.
1282 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
1283 processedErrorMessage = errorMessage.arg(u"null");
1284 } else if (m_typeResolver->equals(actual.storedType(), m_typeResolver->varType())) {
1285 // Since we have verified the type in qqmljstypepropagator.cpp we now know
1286 // that we can only have either undefined or the actual type here. Therefore,
1287 // it's enough to check the QVariant for isValid().
1288 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
1289 needsVarContentConversion = true;
1290 processedErrorMessage = errorMessage.arg(u"undefined");
1291 } else {
1292 reject(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
1293 }
1294
1296 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
1297 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
1299 m_body += u"}\n"_s;
1300 return needsVarContentConversion;
1301}
1302
1303QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
1304 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1305 const QString &variable, const QString &errorMessage)
1306{
1307 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1308 return variable + u".data()"_s;
1309 return contentPointer(actual, variable);
1310}
1311
1312QString QQmlJSCodeGenerator::resolveQObjectPointer(
1313 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1314 const QString &variable, const QString &errorMessage)
1315{
1316 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1317 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1318 return variable;
1319}
1320
1322{
1324 generate_GetLookupHelper(index);
1325}
1326
1327void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
1328{
1330 reject(u"lookup of function property."_s);
1331 return;
1332 }
1333
1336
1337 double value{};
1338 if (name == u"E") {
1339 value = std::exp(1.0);
1340 } else if (name == u"LN10") {
1341 value = log(10.0);
1342 } else if (name == u"LN2") {
1343 value = log(2.0);
1344 } else if (name == u"LOG10E") {
1345 value = log10(std::exp(1.0));
1346 } else if (name == u"LOG2E") {
1347 value = log2(std::exp(1.0));
1348 } else if (name == u"PI") {
1349 value = 3.14159265358979323846;
1350 } else if (name == u"SQRT1_2") {
1351 value = std::sqrt(0.5);
1352 } else if (name == u"SQRT2") {
1353 value = std::sqrt(2.0);
1354 } else {
1355 Q_UNREACHABLE();
1356 }
1357
1360 + u";\n"_s;
1361 return;
1362 }
1363
1366 // If we have an object module prefix, we need to pass through the original object.
1371 + u";\n"_s;
1372 }
1373 return;
1374 }
1375
1376 AccumulatorConverter registers(this);
1377
1380 return;
1381 }
1382
1383 const QString indexString = QString::number(index);
1384 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1386 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1387 const auto accumulatorIn = m_state.accumulatorIn();
1389 const bool isReferenceType = scope->isReferenceType();
1390
1391 switch (m_state.accumulatorOut().variant()) {
1393 if (!isReferenceType) {
1394 // This can happen on incomplete type information. We contextually know that the
1395 // type must be a QObject, but we cannot construct the inheritance chain. Then we
1396 // store it in a generic type. Technically we could even convert it to QObject*, but
1397 // that would be expensive.
1398 reject(u"attached object for non-QObject type"_s);
1399 }
1400
1402 // This can happen if we retroactively determine that the property might not be
1403 // what we think it is (ie, it can be shadowed).
1404 reject(u"attached object of potentially non-QObject base"_s);
1405 }
1406
1407 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1408
1409 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1410 + u", "_s + m_state.accumulatorVariableIn
1411 + u", &"_s + m_state.accumulatorVariableOut + u')';
1412 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1413 + indexString + u", "_s + namespaceString + u", "_s
1415 generateLookup(lookup, initialization);
1416 return;
1417 }
1422 generateTypeLookup(index);
1423 return;
1424 }
1425 default:
1426 break;
1427 }
1428
1430
1432 reject(u"lookup in QJSValue"_s);
1433 } else if (isReferenceType) {
1434 const QString inputPointer = resolveQObjectPointer(
1435 scope, accumulatorIn, m_state.accumulatorVariableIn,
1436 u"Cannot read property '%1' of %2"_s.arg(
1438 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1439 + u", "_s + inputPointer + u", "_s
1441 const QString initialization = u"aotContext->initGetObjectLookup("_s
1442 + indexString + u", "_s + inputPointer
1444 + u')';
1445 const QString preparation = getLookupPreparation(
1447 generateLookup(lookup, initialization, preparation);
1448 } else if ((scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1450 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1451 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1452 if (stored->isListProperty()) {
1454 m_body += conversion(
1457 m_state.accumulatorVariableIn + u".count("_s + u'&'
1459 m_body += u";\n"_s;
1460 } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1465 m_state.accumulatorVariableIn + u".length()"_s)
1466 + u";\n"_s;
1467 } else {
1468 reject(u"access to 'length' property of sequence wrapped in non-sequence"_s);
1469 }
1470 } else if (m_typeResolver->registerIsStoredIn(accumulatorIn,
1472 QString mapLookup = m_state.accumulatorVariableIn + u"["_s
1476 m_state.accumulatorOut(), mapLookup);
1477 m_body += u";\n"_s;
1478 } else {
1480 reject(u"reading from a value that's potentially affected by side effects"_s);
1481
1482 const QString inputContentPointer = resolveValueTypeContentPointer(
1483 scope, accumulatorIn, m_state.accumulatorVariableIn,
1484 u"Cannot read property '%1' of %2"_s.arg(
1486
1487 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1488 + u", "_s + inputContentPointer
1490 + u')';
1491 const QString initialization = u"aotContext->initGetValueLookup("_s
1492 + indexString + u", "_s
1493 + metaObject(scope) + u", "_s
1495 const QString preparation = getLookupPreparation(
1497 generateLookup(lookup, initialization, preparation);
1498 }
1499}
1500
1502{
1504
1505 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
1506 QString accumulatorVarIn = m_state.accumulatorVariableIn;
1507
1508 const auto &annotation = m_annotations[currentInstructionOffset()];
1509 if (accumulatorIn.storedType()->isReferenceType()) {
1510 m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
1511 generateJumpCodeWithTypeConversions(offset);
1512 } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->varType())) {
1513 m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
1514 "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
1515 generateJumpCodeWithTypeConversions(offset);
1516 } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType())) {
1517 m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
1518 "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
1519 generateJumpCodeWithTypeConversions(offset);
1520 } else if (annotation.changedRegisterIndex == Accumulator
1521 && annotation.changedRegister.variant() == QQmlJSRegisterContent::ObjectEnum) {
1522 // Nothing
1523 } else if (m_typeResolver->equals(accumulatorIn.storedType(), m_typeResolver->jsValueType())) {
1524 m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
1525 generateJumpCodeWithTypeConversions(offset);
1526 } else {
1527 Q_UNREACHABLE(); // No other accumulatorIn stored types should be possible
1528 }
1529
1530 generate_GetLookupHelper(index);
1531}
1532
1533void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
1534{
1535 Q_UNUSED(nameIndex)
1536 Q_UNUSED(baseReg)
1537 reject(u"StoreProperty"_s);
1538}
1539
1541 const QQmlJSRegisterContent &content, const QString &arg, int lookup)
1542{
1543 if (m_typeResolver->registerContains(content, content.storedType()))
1544 return QString();
1545
1547 return u"const QMetaType argType = aotContext->lookupResultMetaType("_s
1548 + QString::number(lookup) + u");\n"_s
1549 + u"if (argType.isValid())\n "_s + arg + u".convert(argType)";
1550 }
1551 // TODO: We could make sure they're compatible, for example QObject pointers.
1552 return QString();
1553}
1554
1555
1557{
1559
1560 const QString indexString = QString::number(index);
1563 Q_ASSERT(specific.isConversion());
1564 const QQmlJSScope::ConstPtr originalScope
1565 = m_typeResolver->originalType(specific.conversionResultScope());
1566
1567 if (specific.storedType().isNull()) {
1568 reject(u"SetLookup. Could not find property "
1570 + u" on type "
1571 + originalScope->internalName());
1572 return;
1573 }
1574
1575 // Choose a container that can hold both, the "in" accumulator and what we actually want.
1576 // If the types are all the same because we can all store them as verbatim C++ types,
1577 // the container will also be that type.
1578
1579 QQmlJSRegisterContent property = specific;
1580 if (!m_typeResolver->equals(specific.storedType(), valueType)) {
1581 if (m_typeResolver->isPrimitive(specific.storedType())
1582 && m_typeResolver->isPrimitive(valueType)) {
1583 // Preferably store in QJSPrimitiveValue since we need the content pointer below.
1584 property = property.storedIn(m_typeResolver->jsPrimitiveType());
1585 } else {
1586 property = property.storedIn(m_typeResolver->merge(specific.storedType(), valueType));
1587 }
1588 }
1589
1590 const QString object = registerVariable(baseReg);
1591 m_body += u"{\n"_s;
1592 QString variableIn;
1593 QString variableInType;
1594 QString preparation;
1595 QString argType;
1598 m_body += u"auto converted = "_s
1600 + u";\n"_s;
1601 variableIn = contentPointer(property, u"converted"_s);
1602 variableInType = contentType(property, u"converted"_s);
1603 preparation = setLookupPreparation(property, u"converted"_s, index);
1604 if (preparation.isEmpty())
1605 argType = contentType(property, u"converted"_s);
1606 else
1607 argType = u"argType"_s;
1608 } else {
1611 argType = variableInType;
1612 }
1613
1614 switch (originalScope->accessSemantics()) {
1615 case QQmlJSScope::AccessSemantics::Reference: {
1616 const QString basePointer = resolveQObjectPointer(
1617 originalScope, registerType(baseReg), object,
1618 u"TypeError: Value is %1 and could not be converted to an object"_s);
1619
1620 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1621 + u", "_s + basePointer + u", "_s + variableIn + u')';
1622 const QString initialization = u"aotContext->initSetObjectLookup("_s
1623 + indexString + u", "_s + basePointer + u", "_s + argType + u')';
1624 generateLookup(lookup, initialization, preparation);
1625 break;
1626 }
1627 case QQmlJSScope::AccessSemantics::Sequence: {
1628 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1629 if (propertyName != u"length"_s) {
1630 reject(u"setting non-length property on a sequence type"_s);
1631 break;
1632 }
1633
1634 if (!originalScope->isListProperty()) {
1635 reject(u"resizing sequence types (because of missing write-back)"_s);
1636 break;
1637 }
1638
1639 // We can resize without write back on a list property because it's actually a reference.
1640 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1641 m_body += u"const int end = "_s
1642 + (variableIn.startsWith(u'&') ? variableIn.mid(1) : (u'*' + variableIn))
1643 + u";\n"_s;
1644 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1645 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1646 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1647 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1648 + u";\n"_s;
1649 break;
1650 }
1651 case QQmlJSScope::AccessSemantics::Value: {
1652 const QQmlJSRegisterContent base = registerType(baseReg);
1653 const QString baseContentPointer = resolveValueTypeContentPointer(
1654 originalScope, base, object,
1655 u"TypeError: Value is %1 and could not be converted to an object"_s);
1656
1657 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1658 + u", "_s + baseContentPointer
1659 + u", "_s + variableIn + u')';
1660 const QString initialization = u"aotContext->initSetValueLookup("_s
1661 + indexString + u", "_s + metaObject(originalScope)
1662 + u", "_s + argType + u')';
1663
1664 generateLookup(lookup, initialization, preparation);
1665 generateWriteBack(baseReg);
1666
1667 break;
1668 }
1669 case QQmlJSScope::AccessSemantics::None:
1670 Q_UNREACHABLE();
1671 break;
1672 }
1673
1674 m_body += u"}\n"_s;
1675}
1676
1682
1688
1693
1698
1703
1704QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
1705{
1706 QString types;
1707 QString args;
1708
1712 types = u"QMetaType()"_s;
1713 args = u"nullptr"_s;
1714 } else {
1715 *outVar = u"callResult"_s;
1717 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1721 m_body += u'('
1723 + u')';
1724 }
1725 }
1726 m_body += u";\n";
1727
1730 }
1731
1732 for (int i = 0; i < argc; ++i) {
1733 const QQmlJSRegisterContent content = registerType(argv + i);
1734 const QString var = registerVariable(argv + i);
1735 args += u", "_s + contentPointer(content, var);
1736 types += u", "_s + contentType(content, var);
1737 }
1738
1739 return u"void *args[] = { "_s + args + u" };\n"_s
1740 + u"const QMetaType types[] = { "_s + types + u" };\n"_s;
1741}
1742
1743void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
1744{
1745 if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
1746 return;
1747
1749 m_body += u"std::move(" + outVar + u");\n";
1750}
1751
1753{
1754 Q_UNUSED(name)
1755 Q_UNUSED(argc)
1756 Q_UNUSED(argv)
1758}
1759
1760void QQmlJSCodeGenerator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
1761{
1762 Q_UNUSED(name)
1763 Q_UNUSED(thisObject)
1764 Q_UNUSED(argc)
1765 Q_UNUSED(argv)
1767}
1768
1769void QQmlJSCodeGenerator::generate_CallProperty(int nameIndex, int baseReg, int argc, int argv)
1770{
1771 Q_UNUSED(nameIndex);
1772 Q_UNUSED(baseReg);
1773 Q_UNUSED(argc);
1774 Q_UNUSED(argv);
1775 reject(u"CallProperty"_s);
1776}
1777
1778bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int argc, int argv)
1779{
1780 if (name != u"arg"_s || argc != 1)
1781 return false;
1782
1783 const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
1784 return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
1785 };
1786
1787 const auto ret = [&](const QString &arg) {
1788 const QString expression = convertStored(
1789 registerType(base).storedType(), m_typeResolver->stringType(),
1790 consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
1791 return conversion(
1793 };
1794
1797
1802 else
1804 m_body += u";\n"_s;
1805 return true;
1806}
1807
1808bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
1809{
1810 addInclude(u"QtCore/qcoreapplication.h"_s);
1811
1812 const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
1813 Q_ASSERT(i < argc);
1814 return convertStored(registerType(argv + i).storedType(), type,
1815 consumedRegisterVariable(argv + i));
1816 };
1817
1818 const auto stringArg = [&](int i) {
1819 return i < argc
1820 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
1821 : u"\"\""_s;
1822 };
1823
1824 const auto intArg = [&](int i) {
1825 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
1826 };
1827
1828 const auto stringRet = [&](const QString &expression) {
1829 return conversion(
1831 };
1832
1833 const auto capture = [&]() {
1834 m_body += u"aotContext->captureTranslation();\n"_s;
1835 };
1836
1837 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
1838 Q_ASSERT(argc > 0);
1840 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
1841 return true;
1842 }
1843
1844 if (name == u"QT_TRANSLATE_NOOP"_s) {
1845 Q_ASSERT(argc > 1);
1847 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
1848 return true;
1849 }
1850
1851 if (name == u"qsTrId"_s) {
1852 capture();
1853 // We inline qtTrId() here because in the !QT_CONFIG(translation) case it's unavailable.
1854 // QCoreApplication::translate() is always available in some primitive form.
1855 // Also, this saves a function call.
1857 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
1858 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
1859 return true;
1860 }
1861
1862 if (name == u"qsTr"_s) {
1863 capture();
1865 + stringRet(u"QCoreApplication::translate("_s
1866 + u"aotContext->translationContext().toUtf8().constData(), "_s
1867 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1868 + intArg(2) + u")"_s) + u";\n"_s;
1869 return true;
1870 }
1871
1872 if (name == u"qsTranslate"_s) {
1873 capture();
1875 + stringRet(u"QCoreApplication::translate("_s
1876 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1877 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
1878 return true;
1879 }
1880
1881 return false;
1882}
1883
1884static QString maxExpression(int argc)
1885{
1886 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
1887
1888 QString expression =
1889 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1890 for (int i = 2; i < argc; i++) {
1891 expression +=
1892 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
1893 .arg("arg"_L1 + QString::number(i + 1));
1894 }
1895 expression += "return tmpMax;\n}()"_L1;
1896
1897 return expression;
1898}
1899
1900static QString minExpression(int argc)
1901{
1902 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
1903
1904 QString expression =
1905 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1906 for (int i = 2; i < argc; i++) {
1907 expression +=
1908 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
1909 .arg("arg"_L1 + QString::number(i + 1));
1910 }
1911 expression += "return tmpMin;\n}()"_L1;
1912
1913 return expression;
1914}
1915
1916bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
1917{
1918 addInclude(u"cmath"_s);
1919 addInclude(u"limits"_s);
1920 addInclude(u"QtCore/qalgorithms.h"_s);
1921 addInclude(u"QtCore/qrandom.h"_s);
1922 addInclude(u"QtQml/qjsprimitivevalue.h"_s);
1923
1924 // If the result is not stored, we don't need to generate any code. All the math methods are
1925 // conceptually pure functions.
1927 return true;
1928
1929 m_body += u"{\n"_s;
1930 for (int i = 0; i < argc; ++i) {
1931 m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
1932 registerType(argv + i).storedType(),
1934 + u";\n"_s;
1935 }
1936
1937 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
1938 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
1940
1941 QString expression;
1942
1943 if (name == u"abs" && argc == 1) {
1944 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
1945 } else if (name == u"acos"_s && argc == 1) {
1946 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
1947 } else if (name == u"acosh"_s && argc == 1) {
1948 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
1949 } else if (name == u"asin"_s && argc == 1) {
1950 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
1951 } else if (name == u"asinh"_s && argc == 1) {
1952 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
1953 } else if (name == u"atan"_s && argc == 1) {
1954 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
1955 } else if (name == u"atanh"_s && argc == 1) {
1956 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
1957 } else if (name == u"atan2"_s) {
1958 // TODO: complicated
1959 return false;
1960 } else if (name == u"cbrt"_s && argc == 1) {
1961 expression = u"std::cbrt(arg1)"_s;
1962 } else if (name == u"ceil"_s && argc == 1) {
1963 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
1964 } else if (name == u"clz32"_s && argc == 1) {
1965 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
1966 } else if (name == u"cos"_s && argc == 1) {
1967 expression = u"std::cos(arg1)"_s;
1968 } else if (name == u"cosh"_s && argc == 1) {
1969 expression = u"std::cosh(arg1)"_s;
1970 } else if (name == u"exp"_s && argc == 1) {
1971 expression = u"std::isinf(arg1) "
1972 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
1973 ": std::exp(arg1)"_s.arg(inf);
1974 } else if (name == u"expm1"_s) {
1975 // TODO: complicated
1976 return false;
1977 } else if (name == u"floor"_s && argc == 1) {
1978 expression = u"std::floor(arg1)"_s;
1979 } else if (name == u"fround"_s && argc == 1) {
1980 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
1981 "? arg1 "
1982 ": double(float(arg1))"_s;
1983 } else if (name == u"hypot"_s) {
1984 // TODO: complicated
1985 return false;
1986 } else if (name == u"imul"_s && argc == 2) {
1987 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
1988 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
1989 } else if (name == u"log"_s && argc == 1) {
1990 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
1991 } else if (name == u"log10"_s && argc == 1) {
1992 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
1993 } else if (name == u"log1p"_s && argc == 1) {
1994 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
1995 } else if (name == u"log2"_s && argc == 1) {
1996 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
1997 } else if (name == u"max"_s && argc >= 2) {
1998 expression = maxExpression(argc);
1999 } else if (name == u"min"_s && argc >= 2) {
2000 expression = minExpression(argc);
2001 } else if (name == u"pow"_s) {
2002 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
2003 } else if (name == u"random"_s && argc == 0) {
2004 expression = u"QRandomGenerator::global()->generateDouble()"_s;
2005 } else if (name == u"round"_s && argc == 1) {
2006 expression = u"std::isfinite(arg1) "
2007 "? ((arg1 < 0.5 && arg1 >= -0.5) "
2008 "? std::copysign(0.0, arg1) "
2009 ": std::floor(arg1 + 0.5)) "
2010 ": arg1"_s;
2011 } else if (name == u"sign"_s && argc == 1) {
2012 expression = u"std::isnan(arg1) "
2013 "? %1 "
2014 ": (qIsNull(arg1) "
2015 "? arg1 "
2016 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
2017 } else if (name == u"sin"_s && argc == 1) {
2018 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
2019 } else if (name == u"sinh"_s && argc == 1) {
2020 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
2021 } else if (name == u"sqrt"_s && argc == 1) {
2022 expression = u"std::sqrt(arg1)"_s;
2023 } else if (name == u"tan"_s && argc == 1) {
2024 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
2025 } else if (name == u"tanh"_s && argc == 1) {
2026 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
2027 } else if (name == u"trunc"_s && argc == 1) {
2028 expression = u"std::trunc(arg1)"_s;
2029 } else {
2030 return false;
2031 }
2032
2034
2035 m_body += u";\n"_s;
2036 m_body += u"}\n"_s;
2037 return true;
2038}
2039
2041{
2042 if (method == u"log" || method == u"debug")
2043 return u"QtDebugMsg"_s;
2044 if (method == u"info")
2045 return u"QtInfoMsg"_s;
2046 if (method == u"warn")
2047 return u"QtWarningMsg"_s;
2048 if (method == u"error")
2049 return u"QtCriticalMsg"_s;
2050 return QString();
2051}
2052
2053bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int argv)
2054{
2056 if (type.isEmpty())
2057 return false;
2058
2059 addInclude(u"QtCore/qloggingcategory.h"_s);
2060
2061 m_body += u"{\n";
2062 m_body += u" bool firstArgIsCategory = false;\n";
2063 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
2064
2065 // We could check for internalName == "QQmlLoggingCategory" here, but we don't want to
2066 // because QQmlLoggingCategory is not a builtin. Tying the specific internal name and
2067 // intheritance hierarchy in here would be fragile.
2068 // TODO: We could drop the check for firstArg in some cases if we made some base class
2069 // of QQmlLoggingCategory a builtin.
2070 const bool firstArgIsReference = argc > 0
2072
2073 if (firstArgIsReference) {
2074 m_body += u" QObject *firstArg = ";
2076 firstArg.storedType(),
2077 m_typeResolver->genericType(firstArg.storedType()),
2078 registerVariable(argv));
2079 m_body += u";\n";
2080 }
2081
2082 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
2083 m_body += firstArgIsReference ? u"firstArg" : u"nullptr";
2084 m_body += u", &firstArgIsCategory);\n";
2085 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
2086
2087 m_body += u" const QString message = ";
2088
2089 const auto stringConversion = [&](int i) -> QString {
2090 const QQmlJSScope::ConstPtr stored = m_state.readRegister(argv + i).storedType();
2092 return convertStored(
2093 registerType(argv + i).storedType(),
2095 } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2096 addInclude(u"QtQml/qjslist.h"_s);
2097 return u"u'[' + QJSList(&"_s + registerVariable(argv + i)
2098 + u", aotContext->engine).toString() + u']'"_s;
2099 } else {
2100 reject(u"converting arguments for console method to string"_s);
2101 return QString();
2102 }
2103 };
2104
2105 if (argc > 0) {
2106 if (firstArgIsReference) {
2107 const QString firstArgStringConversion = convertStored(
2108 registerType(argv).storedType(),
2110 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
2111 if (argc > 1)
2112 m_body += u".append(QLatin1Char(' ')))).append(";
2113 else
2114 m_body += u"))";
2115 } else {
2116 m_body += stringConversion(0);
2117 if (argc > 1)
2118 m_body += u".append(QLatin1Char(' ')).append(";
2119 }
2120
2121 for (int i = 1; i < argc; ++i) {
2122 if (i > 1)
2123 m_body += u".append(QLatin1Char(' ')).append("_s;
2124 m_body += stringConversion(i) + u')';
2125 }
2126 } else {
2127 m_body += u"QString()";
2128 }
2129 m_body += u";\n ";
2131 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
2132 m_body += u" }\n";
2133 m_body += u"}\n";
2134 return true;
2135}
2136
2137bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
2138{
2139 const auto intType = m_typeResolver->int32Type();
2140 const auto valueType = registerType(base).storedType()->valueType();
2141 const auto boolType = m_typeResolver->boolType();
2142 const auto stringType = m_typeResolver->stringType();
2143 const auto baseType = registerType(base);
2144
2145 const QString baseVar = registerVariable(base);
2146 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
2147 + name + u"(";
2148
2149 addInclude(u"QtQml/qjslist.h"_s);
2150
2151 if (name == u"includes" && argc > 0 && argc < 3) {
2152 QString call = qjsListMethod
2153 + convertStored(registerType(argv).storedType(), valueType,
2155 if (argc == 2) {
2156 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2157 consumedRegisterVariable(argv + 1));
2158 }
2159 call += u")";
2160
2162 + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
2163 return true;
2164 }
2165
2166 if (name == u"toString" || (name == u"join" && argc < 2)) {
2167 QString call = qjsListMethod;
2168 if (argc == 1) {
2169 call += convertStored(registerType(argv).storedType(), stringType,
2171 }
2172 call += u")";
2173
2175 + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
2176 return true;
2177 }
2178
2179 if (name == u"slice" && argc < 3) {
2180 QString call = qjsListMethod;
2181 for (int i = 0; i < argc; ++i) {
2182 if (i > 0)
2183 call += u", ";
2184 call += convertStored(registerType(argv + i).storedType(), intType,
2185 consumedRegisterVariable(argv + i));
2186 }
2187 call += u")";
2188
2189 const auto outType = baseType.storedType()->isListProperty()
2191 : baseType;
2192
2194 + conversion(outType, m_state.accumulatorOut(), call) + u";\n"_s;
2195 return true;
2196 }
2197
2198 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2199 QString call = qjsListMethod
2200 + convertStored(registerType(argv).storedType(), valueType,
2202 if (argc == 2) {
2203 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2204 consumedRegisterVariable(argv + 1));
2205 }
2206 call += u")";
2207
2209 + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
2210 return true;
2211 }
2212
2213 return false;
2214}
2215
2217{
2219
2221 reject(u"call to untyped JavaScript function"_s);
2222
2224
2225 AccumulatorConverter registers(this);
2226
2227 const QQmlJSRegisterContent baseType = registerType(base);
2229
2231 if (inlineMathMethod(name, argc, argv))
2232 return;
2233 } else if (m_typeResolver->equals(scope, m_typeResolver->consoleObject())) {
2234 if (inlineConsoleMethod(name, argc, argv))
2235 return;
2236 } else if (m_typeResolver->equals(scope, m_typeResolver->stringType())) {
2237 if (inlineStringMethod(name, base, argc, argv))
2238 return;
2239 } else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2240 if (inlineArrayMethod(name, base, argc, argv))
2241 return;
2242 }
2243
2244 if (!scope->isReferenceType()) {
2245 // This is possible, once we establish the right kind of lookup for it
2246 reject(u"call to property '%1' of %2"_s.arg(name, baseType.descriptiveName()));
2247 }
2248
2249 const QString inputPointer = resolveQObjectPointer(
2250 scope, baseType, registerVariable(base),
2251 u"Cannot call method '%1' of %2"_s.arg(name));
2252
2253 const QString indexString = QString::number(index);
2254
2255 m_body += u"{\n"_s;
2256
2257 QString outVar;
2258 m_body += argumentsList(argc, argv, &outVar);
2259 const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
2260 + u", "_s + inputPointer
2261 + u", args, types, "_s + QString::number(argc) + u')';
2262 const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
2263 + indexString + u')';
2264 generateLookup(lookup, initialization);
2265 generateMoveOutVar(outVar);
2266
2267 m_body += u"}\n"_s;
2268}
2269
2271{
2272 Q_UNUSED(name);
2273 Q_UNUSED(argc);
2274 Q_UNUSED(argv);
2275 reject(u"CallName"_s);
2276}
2277
2279{
2280 Q_UNUSED(argc)
2281 Q_UNUSED(argv)
2283}
2284
2286{
2287 Q_UNUSED(index);
2288 Q_UNUSED(argc);
2289 Q_UNUSED(argv);
2290 reject(u"CallGlobalLookup"_s);
2291}
2292
2294{
2296
2298 reject(u"call to untyped JavaScript function"_s);
2299
2304 if (inlineTranslateMethod(name, argc, argv))
2305 return;
2306 }
2307
2308 AccumulatorConverter registers(this);
2309
2310 const QString indexString = QString::number(index);
2311
2312 m_body += u"{\n"_s;
2313 QString outVar;
2314 m_body += argumentsList(argc, argv, &outVar);
2315 const QString lookup = u"aotContext->callQmlContextPropertyLookup("_s + indexString
2316 + u", args, types, "_s + QString::number(argc) + u')';
2317 const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_s
2318 + indexString + u')';
2319 generateLookup(lookup, initialization);
2320 generateMoveOutVar(outVar);
2321
2322 m_body += u"}\n"_s;
2323}
2324
2325void QQmlJSCodeGenerator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
2326{
2327 Q_UNUSED(func)
2328 Q_UNUSED(thisObject)
2329 Q_UNUSED(argc)
2330 Q_UNUSED(argv)
2332}
2333
2334void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc, int argv)
2335{
2336 Q_UNUSED(func)
2337 Q_UNUSED(thisObject)
2338 Q_UNUSED(argc)
2339 Q_UNUSED(argv)
2341}
2342
2344{
2346 Q_UNUSED(func);
2347
2348 const auto original = m_typeResolver->original(m_state.accumulatorOut());
2349
2352 if (argc == 0) {
2353 m_body += conversion(
2355 u"QDateTime::currentDateTime()"_s) + u";\n";
2356 return;
2357 }
2358
2359 if (argc == 1
2362 m_body += conversion(
2364 + u";\n";
2365 return;
2366 }
2367
2368 QString ctorArgs;
2369 constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
2370 for (int i = 0; i < std::min(argc, maxArgc); ++i) {
2371 if (i > 0)
2372 ctorArgs += u", ";
2373 ctorArgs += conversion(
2374 registerType(argv + i), m_state.readRegister(argv + i),
2375 registerVariable(argv + i));
2376 }
2377 m_body += conversion(
2379 u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
2380 return;
2381 }
2382
2384 rejectIfBadArray();
2385
2386 if (argc == 1
2389 addInclude(u"QtQml/qjslist.h"_s);
2390
2391 const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
2392 + u"QLatin1String(\"Invalid array length\"));\n"_s;
2393
2394 const QString indexName = registerVariable(argv);
2395 const auto indexType = m_typeResolver->containedType(registerType(argv));
2396 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
2397 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
2398 + error;
2400 m_body += u"}\n"_s;
2401 } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
2402 m_body += u"if ("_s + indexName + u" < 0) {\n"_s
2403 + error;
2405 m_body += u"}\n"_s;
2406 }
2407
2409 + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
2410 m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
2411 + u", aotContext->engine).resize("_s
2412 + convertStored(
2413 registerType(argv).storedType(), m_typeResolver->sizeType(),
2415 + u");\n"_s;
2416 } else if (!m_error->isValid()) {
2417 generateArrayInitializer(argc, argv);
2418 }
2419 return;
2420 }
2421
2422 reject(u"Construct"_s);
2423}
2424
2426{
2427 Q_UNUSED(func)
2428 Q_UNUSED(argc)
2429 Q_UNUSED(argv)
2431}
2432
2434{
2436 reject(u"SetUnwindHandlerh"_s);
2437}
2438
2440{
2441 reject(u"UnwindDispatch"_s);
2442}
2443
2450
2452{
2453 Q_UNUSED(name)
2455 // Nothing to do here. If we have statically asserted the dtz check in the type propagator
2456 // the value cannot be empty. Otherwise we can't get here.
2457}
2458
2460{
2462
2464 m_body += u"aotContext->engine->throwError("_s
2467 m_state.accumulatorVariableIn) + u");\n"_s;
2469 m_skipUntilNextLabel = true;
2470 resetState();
2471}
2472
2477
2482
2489
2491{
2493 Q_UNUSED(nameIndex)
2494 reject(u"PushCatchContext"_s);
2495}
2496
2501
2507
2512
2518
2523
2525{
2527
2528 // Add an empty block before the closing brace, in case there was a bare label before it.
2529 m_body += u"{}\n}\n"_s;
2530}
2531
2533{
2535
2536 addInclude(u"QtQml/qjslist.h"_s);
2537 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2538 if (!listType.isList())
2539 reject(u"iterator on non-list type"_s);
2540
2541 const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
2542 if (!iteratorType.isProperty()) {
2543 reject(u"using non-iterator as iterator"_s);
2544 return;
2545 }
2546
2547 const QString identifier = QString::number(iteratorType.baseLookupIndex());
2548 const QString iteratorName = m_state.accumulatorVariableOut + u"Iterator" + identifier;
2549 const QString listName = m_state.accumulatorVariableOut + u"List" + identifier;
2550
2551 m_body += u"QJSListFor"_s
2552 + (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
2553 + u"Iterator "_s + iteratorName + u";\n";
2554 m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
2555
2556 m_body += m_state.accumulatorVariableOut + u"->init(";
2557 if (iterator == int(QQmlJS::AST::ForEachType::In)) {
2558 if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forInIteratorPtr()))
2559 reject(u"using non-iterator as iterator"_s);
2560 m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
2561 }
2562 m_body += u");\n";
2563
2564 if (iterator == int(QQmlJS::AST::ForEachType::Of)) {
2565 if (!m_typeResolver->equals(iteratorType.storedType(), m_typeResolver->forOfIteratorPtr()))
2566 reject(u"using non-iterator as iterator"_s);
2567 m_body += u"const auto &" // Rely on life time extension for const refs
2568 + listName + u" = " + consumedAccumulatorVariableIn();
2569 }
2570}
2571
2573{
2575
2577 const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
2578 if (!iteratorContent.isProperty()) {
2579 reject(u"using non-iterator as iterator"_s);
2580 return;
2581 }
2582
2583 const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
2584 const QString iteratorTypeName = iteratorType->internalName();
2585 const QString listName = m_state.accumulatorVariableIn
2586 + u"List" + QString::number(iteratorContent.baseLookupIndex());
2587 QString qjsList;
2589 qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
2590 else if (!m_typeResolver->equals(iteratorType, m_typeResolver->forInIteratorPtr()))
2591 reject(u"using non-iterator as iterator"_s);
2592
2593 m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
2594 m_body += changedRegisterVariable() + u" = "
2595 + conversion(
2596 m_typeResolver->valueType(iteratorContent),
2598 m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
2599 + u";\n";
2600 m_body += u"} else {\n ";
2601 m_body += changedRegisterVariable() + u" = "
2603 m_body += u";\n ";
2604 generateJumpCodeWithTypeConversions(offset);
2605 m_body += u"\n}"_s;
2606}
2607
2609{
2610 Q_UNUSED(iterator)
2611 Q_UNUSED(object)
2614}
2615
2620
2625
2632
2638
2640{
2641 Q_UNUSED(name);
2642 reject(u"TypeofName"_s);
2643}
2644
2646{
2647 reject(u"TypeofValue"_s);
2648}
2649
2650void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
2651{
2652 Q_UNUSED(varName)
2653 Q_UNUSED(isDeletable)
2655}
2656
2658{
2660
2661 rejectIfBadArray();
2662 if (!m_error->isValid())
2663 generateArrayInitializer(argc, args);
2664}
2665
2666void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
2667{
2669
2671 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value) {
2672 reject(u"storing an object literal in a non-value type"_s);
2673 return;
2674 }
2675
2677
2678 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2679 Q_ASSERT(argc >= classSize);
2680
2681 if (m_typeResolver->equals(contained, m_typeResolver->varType())
2683
2684 m_body += m_state.accumulatorVariableOut + u" = QVariantMap {\n";
2685 const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
2686 for (int i = 0; i < classSize; ++i) {
2687 m_body += u"{ "_s
2689 + u", "_s;
2690 const int currentArg = args + i;
2691 const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
2692 const QString consumedArg = consumedRegisterVariable(currentArg);
2693 m_body += convertStored(argType, propType, consumedArg) + u" },\n";
2694 }
2695
2696 for (int i = classSize; i < argc; i += 3) {
2697 const int nameArg = args + i + 1;
2698 m_body += u"{ "_s
2699 + conversion(
2700 registerType(nameArg),
2702 consumedRegisterVariable(nameArg))
2703 + u", "_s;
2704
2705 const int valueArg = args + i + 2;
2707 registerType(valueArg).storedType(),
2708 propType,
2709 consumedRegisterVariable(valueArg))
2710 + u" },\n";
2711 }
2712
2713 m_body += u"};\n";
2714 return;
2715 }
2716
2718 const bool isVariantOrPrimitive = m_typeResolver->equals(stored, m_typeResolver->varType())
2720
2722 m_body += u"()";
2723 } else if (isVariantOrPrimitive) {
2725 } else {
2726 reject(u"storing an object literal in an unsupported container %1"_s
2727 .arg(stored->internalName()));
2728 }
2729 m_body += u";\n";
2730
2731 if (argc == 0)
2732 return;
2733
2734 bool isExtension = false;
2735 if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension)) {
2736 reject(u"storing an object literal in a non-structured value type"_s);
2737 }
2738
2739 const QQmlJSScope::ConstPtr accessor = isExtension
2740 ? contained->extensionType().scope
2741 : contained;
2742
2743 m_body += u"{\n";
2744 m_body += u" const QMetaObject *meta = ";
2745 if (!isExtension && isVariantOrPrimitive)
2746 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
2747 else
2748 m_body += metaObject(accessor);
2749 m_body += u";\n";
2750
2751 for (int i = 0; i < classSize; ++i) {
2752 m_body += u" {\n";
2753 const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
2754 const int currentArg = args + i;
2755 const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
2756 const QQmlJSRegisterContent argType = registerType(currentArg);
2757 const QQmlJSMetaProperty property = contained->property(propName);
2758 const QString consumedArg = consumedRegisterVariable(currentArg);
2759 QString argument = conversion(argType, propType, consumedArg);
2760
2761 if (argument == consumedArg) {
2762 argument = registerVariable(currentArg);
2763 } else {
2764 m_body += u" auto arg = "_s + argument + u";\n";
2765 argument = u"arg"_s;
2766 }
2767
2768 int index = property.index();
2769 if (index == -1)
2770 continue;
2771
2772 m_body += u" void *argv[] = { %1, nullptr };\n"_s
2773 .arg(contentPointer(propType, argument));
2774 m_body += u" meta->d.static_metacall(reinterpret_cast<QObject *>(";
2776 m_body += u"), QMetaObject::WriteProperty, ";
2777 m_body += QString::number(index) + u", argv);\n";
2778 m_body += u" }\n";
2779 }
2780
2781 // This is not implemented because we cannot statically determine the type of the value and we
2782 // don't want to rely on QVariant::convert() since that may give different results than
2783 // the JavaScript coercion. We might still make it work by querying the QMetaProperty
2784 // for its type at run time and runtime coercing to that, but we don't know whether that
2785 // still pays off.
2786 if (argc > classSize)
2787 reject(u"non-literal keys of object literals"_s);
2788
2789 m_body += u"}\n";
2790
2791}
2792
2793void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
2794{
2795 Q_UNUSED(classIndex)
2796 Q_UNUSED(heritage)
2797 Q_UNUSED(computedNames)
2799}
2800
2805
2810
2812{
2813 Q_UNUSED(argIndex)
2815}
2816
2818{
2820
2821 m_body += changedRegisterVariable() + u" = "_s
2823 u"aotContext->thisObject()"_s)
2824 + u";\n"_s;
2825}
2826
2831
2836
2838{
2840
2841 generateJumpCodeWithTypeConversions(offset);
2842 m_skipUntilNextLabel = true;
2843 resetState();
2844}
2845
2847{
2849
2850 m_body += u"if ("_s;
2853 m_body += u") "_s;
2854 generateJumpCodeWithTypeConversions(offset);
2855}
2856
2858{
2860
2861 m_body += u"if (!"_s;
2864 m_body += u") "_s;
2865 generateJumpCodeWithTypeConversions(offset);
2866}
2867
2869{
2871
2872 m_body += u"if (!context->engine->hasException()) "_s;
2873 generateJumpCodeWithTypeConversions(offset);
2874}
2875
2881
2883{
2885
2886 generateExceptionCheck();
2887}
2888
2890{
2892 generateEqualityOperation(
2893 m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, false);
2894}
2895
2897{
2898 INJECT_TRACE_INFO(generate_CmlNeNull);
2899 generateEqualityOperation(
2900 m_typeResolver->globalType(m_typeResolver->nullType()), QString(), u"equals"_s, true);
2901}
2902
2904 const QQmlJSRegisterContent &content, const QString &var, int lookup)
2905{
2906 if (m_typeResolver->registerContains(content, content.storedType()))
2907 return QString();
2908
2910 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
2911 + QString::number(lookup) + u"))"_s;
2912 }
2913 // TODO: We could make sure they're compatible, for example QObject pointers.
2914 return QString();
2915}
2916
2918{
2919 const QQmlJSScope::ConstPtr stored = content.storedType();
2920 if (m_typeResolver->registerContains(content, stored))
2921 return u'&' + var;
2922
2925 return var + u".data()"_s;
2926 }
2927
2928 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2929 return u'&' + var;
2930
2931 if (m_typeResolver->isNumeric(content.storedType())
2933 return u'&' + var;
2934 }
2935
2936 if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
2937 return u'&' + var;
2938
2939 reject(u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
2940 return QString();
2941}
2942
2944{
2945 const QQmlJSScope::ConstPtr stored = content.storedType();
2946 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(content);
2947 if (m_typeResolver->equals(contained, stored))
2948 return metaTypeFromType(stored);
2949
2952 return var + u".metaType()"_s; // We expect the container to be initialized
2953 }
2954
2955 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2956 return metaType(contained);
2957
2958 const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
2959 if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
2960 return metaTypeFromType(nonComposite->baseType());
2961
2962 if (stored->isListProperty() && contained->isListProperty())
2963 return metaType(contained);
2964
2965 reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
2966 return QString();
2967}
2968
2970{
2972
2973 generateEqualityOperation(
2975 u"equals"_s, false);
2976}
2977
2979{
2981
2982 generateEqualityOperation(
2984 u"equals"_s, true);
2985}
2986
2988{
2990 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, false);
2991}
2992
2994{
2996 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, true);
2997}
2998
3000{
3002 generateCompareOperation(lhs, u">"_s);
3003}
3004
3006{
3008 generateCompareOperation(lhs, u">="_s);
3009}
3010
3012{
3014 generateCompareOperation(lhs, u"<"_s);
3015}
3016
3018{
3020 generateCompareOperation(lhs, u"<="_s);
3021}
3022
3024{
3026 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, false);
3027}
3028
3030{
3032 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, true);
3033}
3034
3036{
3037 Q_UNUSED(lhs)
3038 reject(u"CmpIn"_s);
3039}
3040
3046
3048{
3050
3051 const QString input = registerVariable(lhs);
3052 const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
3053 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
3054
3055 // If the original output is a conversion, we're supposed to check for the contained
3056 // type and if it doesn't match, set the result to null or undefined.
3057 const QQmlJSRegisterContent originalContent = m_typeResolver->original(outputContent);
3058 const QQmlJSScope::ConstPtr target = originalContent.storedType()->isReferenceType()
3059 ? m_typeResolver->containedType(originalContent)
3061
3062 if (!target) {
3063 reject(u"type assertion to unknown type"_s);
3064 return;
3065 }
3066
3067 const bool isTrivial = m_typeResolver->inherits(
3069
3071
3072 if (!isTrivial && target->isReferenceType()) {
3073 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
3074 const QString inputConversion = inputContent.storedType()->isReferenceType()
3075 ? input
3076 : convertStored(inputContent.storedType(), genericContained, input);
3077
3078 if (target->isComposite() && m_typeResolver->equals(
3080 m_body += conversion(
3081 genericContained, outputContent,
3082 m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
3083 } else {
3084 m_body += conversion(
3085 genericContained, outputContent,
3086 u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
3087 }
3088 m_body += u";\n"_s;
3089 return;
3090 }
3091
3094
3096 m_typeResolver->original(inputContent));
3097
3099 m_body += input + u".metaType() == "_s + metaType(target)
3100 + u" ? " + conversion(inputContent, outputContent, input)
3101 + u" : " + conversion(
3103 outputContent, QString());
3104 m_body += u";\n"_s;
3105 return;
3106 }
3107 }
3108
3109 if (isTrivial) {
3110 // No actual conversion necessary. The 'as' is a no-op
3111 m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
3112 return;
3113 }
3114
3115 reject(u"non-trivial value type assertion"_s);
3116}
3117
3119{
3121 generateUnaryOperation(u"!"_s);
3122}
3123
3125{
3127 generateUnaryOperation(u"+"_s);
3128}
3129
3131{
3133 generateUnaryOperation(u"-"_s);
3134}
3135
3137{
3139 generateUnaryOperation(u"~"_s);
3140}
3141
3143{
3145 generateInPlaceOperation(u"++"_s);
3146}
3147
3149{
3151 generateInPlaceOperation(u"--"_s);
3152}
3153
3155{
3157 generateArithmeticOperation(lhs, u"+"_s);
3158}
3159
3161{
3163 generateArithmeticOperation(lhs, u"&"_s);
3164}
3165
3167{
3169 generateArithmeticOperation(lhs, u"|"_s);
3170}
3171
3173{
3175 generateArithmeticOperation(lhs, u"^"_s);
3176}
3177
3179{
3180 INJECT_TRACE_INFO(generate_BitUShr);
3181 generateShiftOperation(lhs, u">>"_s);
3182}
3183
3185{
3187 generateShiftOperation(lhs, u">>"_s);
3188}
3189
3191{
3193 generateShiftOperation(lhs, u"<<"_s);
3194}
3195
3197{
3199 generateArithmeticConstOperation(rhs, u"&"_s);
3200}
3201
3203{
3205 generateArithmeticConstOperation(rhs, u"|"_s);
3206}
3207
3209{
3211 generateArithmeticConstOperation(rhs, u"^"_s);
3212}
3213
3215{
3217 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3218}
3219
3221{
3223 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3224}
3225
3227{
3229 generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
3230}
3231
3233{
3235
3236 const QString lhsString = conversion(
3238 const QString rhsString = conversion(
3241
3242 Q_ASSERT(m_error->isValid() || !lhsString.isEmpty());
3243 Q_ASSERT(m_error->isValid() || !rhsString.isEmpty());
3244
3247 m_body += conversion(
3248 originalOut, m_state.accumulatorOut(),
3249 u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
3250 m_body += u";\n"_s;
3251}
3252
3254{
3256 generateArithmeticOperation(lhs, u"*"_s);
3257}
3258
3260{
3262 generateArithmeticOperation(lhs, u"/"_s);
3263}
3264
3266{
3268
3269 const auto lhsVar = convertStored(
3270 registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
3272 const auto rhsVar = convertStored(
3275 Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
3276 Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
3277
3279 m_body += u" = "_s;
3281 u'(' + lhsVar + u" % "_s + rhsVar + u')');
3282 m_body += u";\n"_s;
3283}
3284
3286{
3288 generateArithmeticOperation(lhs, u"-"_s);
3289}
3290
3292{
3293 Q_UNUSED(firstReg)
3295 // Ignore. We reject uninitialized values anyway.
3296}
3297
3302
3308
3311{
3313 const auto accumulatorIn = m_state.registers.find(Accumulator);
3314 if (accumulatorIn != m_state.registers.end()
3315 && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
3316 const QQmlJSRegisterContent &content = accumulatorIn.value().content;
3317 m_state.accumulatorVariableIn = m_registerVariables.value(RegisterVariablesKey {
3318 content.storedType()->internalName(),
3320 content.resultLookupIndex()
3321 }).variableName;
3323 } else {
3325 }
3326
3327 auto labelIt = m_labels.constFind(currentInstructionOffset());
3328 if (labelIt != m_labels.constEnd()) {
3329 m_body += *labelIt + u":;\n"_s;
3330 m_skipUntilNextLabel = false;
3331 } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
3332 return SkipInstruction;
3333 }
3334
3337 else
3339
3340 // If the accumulator type is valid, we want an accumulator variable.
3341 // If not, we don't want one.
3347
3348 // If the instruction has no side effects and doesn't write any register, it's dead.
3349 // We might still need the label, though, and the source code comment.
3350 if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) {
3351 generateJumpCodeWithTypeConversions(0);
3352 return SkipInstruction;
3353 }
3354
3355 return ProcessInstruction;
3356}
3357
3359{
3360 if (!m_skipUntilNextLabel)
3361 generateJumpCodeWithTypeConversions(0);
3362}
3363
3365{
3366 m_body += u"aotContext->setInstructionPointer("_s
3367 + QString::number(nextInstructionOffset()) + u");\n"_s;
3368}
3369
3370void QQmlJSCodeGenerator::generateExceptionCheck()
3371{
3372 m_body += u"if (aotContext->engine->hasError()) {\n"_s;
3374 m_body += u"}\n"_s;
3375}
3376
3377void QQmlJSCodeGenerator::generateEqualityOperation(
3378 const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent,
3379 const QString &lhsName, const QString &rhsName, const QString &function, bool invert)
3380{
3381 const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
3382 const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
3383
3384 const auto rhsContained = rhsIsOptional
3386 : m_typeResolver->containedType(rhsContent);
3387
3388 const auto lhsContained = lhsIsOptional
3390 : m_typeResolver->containedType(lhsContent);
3391
3392 const bool isStrict = function == "strictlyEquals"_L1;
3393 const bool strictlyComparableWithVar
3394 = isStrict && canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
3395 auto isComparable = [&]() {
3396 if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
3397 return true;
3398 if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
3399 return true;
3400 if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
3401 return true;
3402 if (strictlyComparableWithVar)
3403 return true;
3404 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
3405 return true;
3406 if (canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
3407 return true;
3408 return false;
3409 };
3410
3411 const auto retrieveOriginal = [this](const QQmlJSRegisterContent &content) {
3412 const auto contained = m_typeResolver->containedType(content);
3413 const auto original = m_typeResolver->original(content);
3414 const auto containedOriginal = m_typeResolver->containedType(original);
3415
3417 m_typeResolver->genericType(containedOriginal), original.storedType())) {
3418 // The original type doesn't need any wrapping.
3419 return original;
3420 } else if (m_typeResolver->equals(contained, containedOriginal)) {
3421 if (original.isConversion()) {
3422 // The original conversion origins are more accurate
3423 return original.storedIn(content.storedType());
3424 }
3425 } else if (m_typeResolver->canHold(contained, containedOriginal)) {
3426 return original.storedIn(content.storedType());
3427 }
3428
3429 return content;
3430 };
3431
3432 if (!isComparable()) {
3433 QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
3434 QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
3435 if (lhsOriginal != lhsContent || rhsOriginal != rhsContent) {
3436 // If either side is simply a wrapping of a specific type into a more general one, we
3437 // can compare the original types instead. You can't nest wrappings after all.
3438 generateEqualityOperation(lhsOriginal, rhsOriginal,
3439 conversion(lhsContent.storedType(), lhsOriginal, lhsName),
3440 conversion(rhsContent.storedType(), rhsOriginal, rhsName),
3441 function, invert);
3442 return;
3443 }
3444
3445 reject(u"incomparable types %1 and %2"_s.arg(
3446 rhsContent.descriptiveName(), lhsContent.descriptiveName()));
3447 }
3448
3449 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
3450 const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
3451
3452 if (strictlyComparableWithVar) {
3453 // Determine which side is holding a storable type
3454 if (!lhsName.isEmpty() && rhsName.isEmpty()) {
3455 // lhs register holds var type and rhs is not storable
3456 generateVariantEqualityComparison(rhsContent, lhsName, invert);
3457 return;
3458 }
3459
3460 if (!rhsName.isEmpty() && lhsName.isEmpty()) {
3461 // lhs content is not storable and rhs is var type
3462 generateVariantEqualityComparison(lhsContent, rhsName, invert);
3463 return;
3464 }
3465
3467 generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
3468 return;
3469 }
3470
3472 generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
3473 return;
3474 }
3475
3476 // It shouldn't be possible to get here because optional null should be stored in
3477 // QJSPrimitiveValue, not in QVariant. But let's rather be safe than sorry.
3478 reject(u"comparison of optional null"_s);
3479 }
3480
3481 const auto comparison = [&]() -> QString {
3482 const auto primitive = m_typeResolver->jsPrimitiveType();
3483 const QString sign = invert ? u" != "_s : u" == "_s;
3484
3485 if (m_typeResolver->equals(lhsType, rhsType)
3486 && !m_typeResolver->equals(lhsType, primitive)
3487 && !m_typeResolver->equals(lhsType, m_typeResolver->varType())) {
3488
3489 // Straight forward comparison of equal types,
3490 // except QJSPrimitiveValue which has two comparison functions.
3491
3492 if (isTypeStorable(m_typeResolver, lhsType))
3493 return lhsName + sign + rhsName;
3494
3495 // null === null and undefined === undefined
3496 return invert ? u"false"_s : u"true"_s;
3497 }
3498
3499 if (canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
3500 // Comparison of QObject-derived with nullptr or different QObject-derived.
3501 return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
3502 + sign
3503 + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
3504 }
3505
3506 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
3507 // Comparison of optional QObject-derived with nullptr or different QObject-derived.
3508 // Mind that null == undefined but null !== undefined
3509 // Therefore the isStrict dance.
3510
3512 if (isStrict) {
3513 if (lhsIsOptional) {
3514 if (rhsIsOptional) {
3515 // If both are invalid we're fine
3516 result += u"(!"_s
3517 + lhsName + u".isValid() && !"_s
3518 + rhsName + u".isValid()) || "_s;
3519 }
3520
3521 result += u'(' + lhsName + u".isValid() && "_s;
3522 } else {
3523 result += u'(';
3524 }
3525
3526 if (rhsIsOptional) {
3527 result += rhsName + u".isValid() && "_s;
3528 }
3529 } else {
3530 result += u'(';
3531 }
3532
3533 // We do not implement comparison with explicit undefined, yet. Only with null.
3536
3537 const auto resolvedName = [&](const QString name) -> QString {
3538 // If isStrict we check validity already before.
3539 const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
3540 return isStrict
3541 ? content
3542 : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
3543 };
3544
3545 const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
3546 const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
3547
3548 return (invert ? u"!("_s : u"("_s) + result
3549 + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
3550 + u" == "_s
3551 + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
3552 + u"))"_s;
3553 }
3554
3555 if ((m_typeResolver->isUnsignedInteger(rhsType)
3556 && m_typeResolver->isUnsignedInteger(lhsType))
3557 || (m_typeResolver->isSignedInteger(rhsType)
3558 && m_typeResolver->isSignedInteger(lhsType))) {
3559 // Both integers of same signedness: Let the C++ compiler perform the type promotion
3560 return lhsName + sign + rhsName;
3561 }
3562
3564 && m_typeResolver->isIntegral(lhsType)) {
3565 // Integral and bool: We can promote the bool to the integral type
3566 return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
3567 }
3568
3570 && m_typeResolver->isIntegral(rhsType)) {
3571 // Integral and bool: We can promote the bool to the integral type
3572 return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
3573 }
3574
3575 if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
3576 // Both numbers: promote them to double
3577 return convertStored(lhsType, m_typeResolver->realType(), lhsName)
3578 + sign
3579 + convertStored(rhsType, m_typeResolver->realType(), rhsName);
3580 }
3581
3582 // If none of the above matches, we have to use QJSPrimitiveValue
3583 return (invert ? u"!"_s : QString())
3584 + convertStored(lhsType, primitive, lhsName)
3585 + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
3586 };
3587
3590 m_body += u";\n"_s;
3591}
3592
3593void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
3594{
3596
3597 const auto lhsType = registerType(lhs);
3598 const QQmlJSScope::ConstPtr compareType =
3602
3603 m_body += conversion(
3605 convertStored(registerType(lhs).storedType(), compareType,
3607 + u' ' + cppOperator + u' '
3608 + convertStored(m_state.accumulatorIn().storedType(), compareType,
3610 m_body += u";\n"_s;
3611}
3612
3613void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
3614{
3615 generateArithmeticOperation(
3620 cppOperator);
3621}
3622
3623void QQmlJSCodeGenerator::generateShiftOperation(int lhs, const QString &cppOperator)
3624{
3625 generateArithmeticOperation(
3629 consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
3630 cppOperator);
3631}
3632
3633void QQmlJSCodeGenerator::generateArithmeticOperation(
3634 const QString &lhs, const QString &rhs, const QString &cppOperator)
3635{
3636 Q_ASSERT(m_error->isValid() || !lhs.isEmpty());
3637 Q_ASSERT(m_error->isValid() || !rhs.isEmpty());
3638
3641 m_body += u" = "_s;
3642 m_body += conversion(
3643 originalOut, m_state.accumulatorOut(),
3644 u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
3645 m_body += u";\n"_s;
3646}
3647
3648void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
3649{
3650 generateArithmeticOperation(
3655 cppOperator);
3656}
3657
3658void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
3659{
3660 const auto var = conversion(m_state.accumulatorIn(),
3663
3665 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3666 return;
3667 }
3668
3669 const auto original = m_typeResolver->original(m_state.accumulatorOut());
3670 if (m_state.accumulatorOut() == original) {
3671 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3673 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3674 return;
3675 }
3676
3678 original, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
3679}
3680
3681void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
3682{
3683 {
3684 // If actually in place, we cannot consume the variable.
3688 m_body += cppOperator + var + u";\n"_s;
3689 return;
3690 }
3691 }
3692
3695
3696 const auto original = m_typeResolver->original(m_state.accumulatorOut());
3697 if (m_state.accumulatorOut() == original) {
3698 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3699 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3700 return;
3701 }
3702
3703 m_body += u"{\n"_s;
3704 m_body += u"auto converted = "_s + var + u";\n"_s;
3706 original, m_state.accumulatorOut(), u'('
3707 + cppOperator + u"converted)"_s) + u";\n"_s;
3708 m_body += u"}\n"_s;
3709}
3710
3711void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
3712 const QString &resultPreparation)
3713{
3714 m_body += u"#ifndef QT_NO_DEBUG\n"_s;
3716 m_body += u"#endif\n"_s;
3717
3718 if (!resultPreparation.isEmpty())
3719 m_body += resultPreparation + u";\n"_s;
3720 m_body += u"while (!"_s + lookup + u") {\n"_s;
3721
3722 m_body += u"#ifdef QT_NO_DEBUG\n"_s;
3724 m_body += u"#endif\n"_s;
3725
3726 m_body += initialization + u";\n"_s;
3727 generateExceptionCheck();
3728 if (!resultPreparation.isEmpty())
3729 m_body += resultPreparation + u";\n"_s;
3730 m_body += u"}\n"_s;
3731}
3732
3733void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset)
3734{
3735 QString conversionCode;
3736 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
3737 const auto annotation = m_annotations.find(absoluteOffset);
3738 if (static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
3739 const auto &conversions = annotation->second.typeConversions;
3740
3741 for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
3742 regIt != regEnd; ++regIt) {
3743 const QQmlJSRegisterContent targetType = regIt.value().content;
3744 if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
3745 continue;
3746
3747 const int registerIndex = regIt.key();
3748 const auto variable = m_registerVariables.constFind(RegisterVariablesKey {
3749 targetType.storedType()->internalName(),
3750 registerIndex,
3751 targetType.resultLookupIndex()
3752 });
3753
3754 if (variable == m_registerVariables.constEnd())
3755 continue;
3756
3757 QQmlJSRegisterContent currentType;
3758 QString currentVariable;
3759 if (registerIndex == m_state.changedRegisterIndex()) {
3760 currentVariable = changedRegisterVariable();
3761 if (variable->variableName == currentVariable)
3762 continue;
3763
3764 currentType = m_state.changedRegister();
3765 currentVariable = u"std::move("_s + currentVariable + u')';
3766 } else {
3767 const auto it = m_state.registers.find(registerIndex);
3768 if (it == m_state.registers.end()
3769 || variable->variableName == registerVariable(registerIndex)) {
3770 continue;
3771 }
3772
3773 currentType = it.value().content;
3774 currentVariable = consumedRegisterVariable(registerIndex);
3775 }
3776
3777 // Actually == here. We want the jump code also for equal types
3778 if (currentType == targetType)
3779 continue;
3780
3781 conversionCode += variable->variableName;
3782 conversionCode += u" = "_s;
3783 conversionCode += conversion(currentType, targetType, currentVariable);
3784 conversionCode += u";\n"_s;
3785 }
3786 }
3787
3788 if (relativeOffset) {
3789 auto labelIt = m_labels.find(absoluteOffset);
3790 if (labelIt == m_labels.end())
3791 labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
3792 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
3793 }
3794
3795 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
3796}
3797
3799{
3800 const QQmlJSRegisterContent &content = registerType(index);
3801 const auto it = m_registerVariables.constFind(RegisterVariablesKey {
3802 content.storedType()->internalName(),
3803 index,
3804 content.resultLookupIndex()
3805 });
3806 if (it != m_registerVariables.constEnd())
3807 return it->variableName;
3808
3809 return QString();
3810}
3811
3813{
3814 for (auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
3815 if (it.key().lookupIndex == lookupIndex)
3816 return it->variableName;
3817 }
3818 return QString();
3819}
3820
3822{
3824 if (var.isEmpty() || !shouldMoveRegister(index))
3825 return var;
3826 return u"std::move(" + var + u")";
3827}
3828
3835
3837{
3838 const QQmlJSRegisterContent &changedRegister = m_state.changedRegister();
3839
3840 const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
3841 if (storedType.isNull())
3842 return QString();
3843
3844 return m_registerVariables.value(RegisterVariablesKey {
3845 storedType->internalName(),
3847 changedRegister.resultLookupIndex()
3848 }).variableName;
3849}
3850
3852{
3853 auto it = m_state.registers.find(index);
3854 if (it != m_state.registers.end())
3855 return it.value().content;
3856
3857 return QQmlJSRegisterContent();
3858}
3859
3861{
3862 auto it = m_state.lookups.find(lookupIndex);
3863 if (it != m_state.lookups.end())
3864 return it.value().content;
3865
3866 return QQmlJSRegisterContent();
3867}
3868
3874
3876 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
3877{
3878 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(to);
3879
3880 // If from is QJSPrimitiveValue and to contains a primitive we coerce using QJSPrimitiveValue
3882 && m_typeResolver->isPrimitive(to)) {
3883
3884 QString primitive = [&]() {
3886 return variable;
3887
3888 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
3889 if (m_typeResolver->equals(contained, m_typeResolver->boolType()))
3890 return conversion.arg(u"Boolean"_s);
3891 if (m_typeResolver->isIntegral(to))
3892 return conversion.arg(u"Integer"_s);
3893 if (m_typeResolver->isNumeric(to))
3894 return conversion.arg(u"Double"_s);
3895 if (m_typeResolver->equals(contained, m_typeResolver->stringType()))
3896 return conversion.arg(u"String"_s);
3897 reject(u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
3898 return QString();
3899 }();
3900
3901 if (primitive.isEmpty())
3902 return primitive;
3903
3904 return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
3905 }
3906
3907 if (m_typeResolver->registerIsStoredIn(to, contained)
3909 || to.storedType()->isReferenceType()
3910 || m_typeResolver->registerContains(from, contained)) {
3911 // If:
3912 // * the output is not actually wrapped at all, or
3913 // * the output is stored in a numeric type (as there are no internals to a number), or
3914 // * the output is a QObject pointer, or
3915 // * we merely wrap the value into a new container,
3916 // we can convert by stored type.
3917 return convertStored(from.storedType(), to.storedType(), variable);
3918 } else {
3919 return convertContained(from, to, variable);
3920 }
3921}
3922
3924 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
3925{
3926 // TODO: most values can be moved, which is much more efficient with the common types.
3927 // add a move(from, to, variable) function that implements the moves.
3928 Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
3929
3930 const auto jsValueType = m_typeResolver->jsValueType();
3931 const auto varType = m_typeResolver->varType();
3932 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
3933 const auto boolType = m_typeResolver->boolType();
3934
3935 auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
3936 if (m_typeResolver->equals(to, boolType))
3937 return u"false"_s;
3939 return u"0"_s;
3941 return u"0u"_s;
3942 return QString();
3943 };
3944
3946 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3947 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3948 const QString zero = zeroBoolOrInt(to);
3949 if (!zero.isEmpty())
3950 return zero;
3952 return u"std::numeric_limits<float>::quiet_NaN()"_s;
3954 return u"std::numeric_limits<double>::quiet_NaN()"_s;
3956 return QQmlJSUtils::toLiteral(u"undefined"_s);
3958 return u"QVariant()"_s;
3960 return u"QJSValue();"_s;
3962 return u"QJSPrimitiveValue()"_s;
3963 if (m_typeResolver->equals(from, to))
3964 return QString();
3965 }
3966
3968 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3969 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3970 if (m_typeResolver->equals(to, jsValueType))
3971 return u"QJSValue(QJSValue::NullValue)"_s;
3972 if (m_typeResolver->equals(to, jsPrimitiveType))
3973 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
3974 if (m_typeResolver->equals(to, varType))
3975 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
3976 const QString zero = zeroBoolOrInt(to);
3977 if (!zero.isEmpty())
3978 return zero;
3980 return u"0.0f"_s;
3982 return u"0.0"_s;
3984 return QQmlJSUtils::toLiteral(u"null"_s);
3985 if (m_typeResolver->equals(from, to))
3986 return QString();
3987 reject(u"Conversion from null to %1"_s.arg(to->internalName()));
3988 }
3989
3990 if (m_typeResolver->equals(from, to))
3991 return variable;
3992
3993 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
3994 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
3995 // Compare internalName here. The same C++ type can be exposed muliple times in
3996 // different QML types. However, the C++ names have to be unique. We can always
3997 // static_cast to those.
3998
3999 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
4000 // We still have to cast as other execution paths may result in different types.
4001 if (base->internalName() == to->internalName())
4002 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4003 }
4004 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
4005 if (base->internalName() == from->internalName())
4006 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4007 }
4008 } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
4009 return u'(' + variable + u" != nullptr)"_s;
4010 }
4011 }
4012
4013 auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
4014 return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
4015 };
4016
4017 if (isJsValue(from) && isJsValue(to))
4018 return variable;
4019
4020 const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
4023 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
4024 };
4025
4029 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
4031 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
4033 return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
4034 }
4035
4036 if (isBoolOrNumber(from) && isBoolOrNumber(to))
4037 return to->internalName() + u'(' + variable + u')';
4038
4039
4040 if (m_typeResolver->equals(from, jsPrimitiveType)) {
4042 return variable + u".toDouble()"_s;
4043 if (m_typeResolver->equals(to, boolType))
4044 return variable + u".toBoolean()"_s;
4047 return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
4048 }
4049 if (m_typeResolver->isIntegral(to))
4050 return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
4052 return variable + u".toString()"_s;
4053 if (m_typeResolver->equals(to, jsValueType))
4054 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
4055 if (m_typeResolver->equals(to, varType))
4056 return variable + u".toVariant()"_s;
4057 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4058 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4059 }
4060
4061 if (isJsValue(from)) {
4062 if (m_typeResolver->equals(to, jsPrimitiveType))
4063 return variable + u".toPrimitive()"_s;
4064 if (m_typeResolver->equals(to, varType))
4065 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
4066 return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
4067 }
4068
4069 if (m_typeResolver->equals(to, jsPrimitiveType)) {
4070 // null and undefined have been handled above already
4073
4078 return u"QJSPrimitiveValue("_s + variable + u')';
4079 } else if (m_typeResolver->equals(from, m_typeResolver->int16Type())
4083 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
4084 } else if (m_typeResolver->isNumeric(from)) {
4085 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
4086 }
4087 }
4088
4089 if (m_typeResolver->equals(to, jsValueType))
4090 return u"aotContext->engine->toScriptValue("_s + variable + u')';
4091
4092 if (m_typeResolver->equals(from, varType)) {
4094 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
4095 return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
4096 + variable + u')';
4097 }
4098
4099 if (m_typeResolver->equals(to, varType))
4100 return u"QVariant::fromValue("_s + variable + u')';
4101
4104 return variable + u".toString()"_s;
4105 }
4106
4109 return u"QUrl("_s + variable + u')';
4110 }
4111
4114 return u"QString::fromUtf8("_s + variable + u')';
4115 }
4116
4119 return variable + u".toUtf8()"_s;
4120 }
4121
4122 for (const auto &originType : {
4126 if (m_typeResolver->equals(from, originType)) {
4127 for (const auto &targetType : {
4133 if (m_typeResolver->equals(to, targetType)) {
4134 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
4135 originType->internalName(), targetType->internalName(), variable);
4136 }
4137 }
4138 break;
4139 }
4140 }
4141
4142 const auto retrieveFromPrimitive = [&](
4143 const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
4144 {
4146 return expression + u".toBoolean()"_s;
4148 return expression + u".toInteger()"_s;
4150 return u"uint("_s + expression + u".toInteger())"_s;
4152 return expression + u".toDouble()"_s;
4154 return u"float("_s + expression + u".toDouble())"_s;
4156 return expression + u".toString()"_s;
4157 return QString();
4158 };
4159
4160 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
4161 const QString retrieve = retrieveFromPrimitive(
4163 if (!retrieve.isEmpty())
4164 return retrieve;
4165 }
4166
4168 return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
4169 + castTargetName(to) + u">("_s + variable + u')';
4170 }
4171
4172 // Any value type is a non-null JS 'object' and therefore coerces to true.
4174 // All the interesting cases are already handled above:
4177 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
4178 Q_ASSERT(!isBoolOrNumber(from));
4179
4180 return u"true"_s;
4181 }
4182
4183 if (m_typeResolver->areEquivalentLists(from, to))
4184 return variable;
4185
4186 if (from->isListProperty()
4187 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
4188 && to->valueType()->isReferenceType()
4189 && !to->isListProperty()) {
4190 return variable + u".toList<"_s + to->internalName() + u">()"_s;
4191 }
4192
4193 bool isExtension = false;
4194 if (m_typeResolver->canPopulate(to, from, &isExtension)) {
4195 reject(u"populating "_s + to->internalName() + u" from "_s + from->internalName());
4196 } else if (const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
4197 ctor.isValid()) {
4198 const auto argumentTypes = ctor.parameters();
4199 return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
4200 + u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
4201 }
4202
4204 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
4205 addInclude(u"QtQml/qjslist.h"_s);
4206
4207 // Extend the life time of whatever variable is across the call to toString().
4208 // variable may be an rvalue.
4209 return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
4210 + variable + u')';
4211 }
4212
4213 // TODO: add more conversions
4214
4215 reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
4216 return QString();
4217}
4218
4220{
4221 const QQmlJSScope::ConstPtr containedFrom = m_typeResolver->containedType(from);
4222 const QQmlJSScope::ConstPtr containedTo = m_typeResolver->containedType(to);
4223
4224 // Those should be handled before, by convertStored().
4226 Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo));
4228 Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo));
4229
4232 reject(u"internal conversion into unsupported wrapper type."_s);
4233 return QString();
4234 }
4235
4236 bool isExtension = false;
4237 if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
4238 reject(u"populating "_s + containedTo->internalName()
4239 + u" from "_s + containedFrom->internalName());
4240 return QString();
4241 } else if (const auto ctor = m_typeResolver->selectConstructor(
4242 containedTo, containedFrom, &isExtension); ctor.isValid()) {
4243 const auto argumentTypes = ctor.parameters();
4244 const QQmlJSScope::ConstPtr argumentType = argumentTypes[0].type();
4245
4246 // We need to store the converted argument in a temporary
4247 // because it might not be an lvalue.
4248
4249 QString input;
4250 QString argPointer;
4251
4252 if (m_typeResolver->equals(argumentType, containedFrom)) {
4253 input = variable;
4254 argPointer = contentPointer(from, u"arg"_s);
4255 } else {
4260 argPointer = contentPointer(argument, u"arg"_s);
4261 }
4262
4263 return u"[&](){ auto arg = " + input
4264 + u"; return aotContext->constructValueType("_s + metaType(containedTo)
4265 + u", "_s + metaObject(
4266 isExtension ? containedTo->extensionType().scope : containedTo)
4267 + u", "_s + QString::number(int(ctor.constructorIndex()))
4268 + u", "_s + argPointer + u"); }()"_s;
4269 }
4270
4271 const auto originalFrom = m_typeResolver->original(from);
4272 const auto containedOriginalFrom = m_typeResolver->containedType(originalFrom);
4273 if (!m_typeResolver->equals(containedFrom, containedOriginalFrom)
4274 && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
4275 // If from is simply a wrapping of a specific type into a more general one, we can convert
4276 // the original type instead. You can't nest wrappings after all.
4277 return conversion(originalFrom.storedIn(from.storedType()), to, variable);
4278 }
4279
4280 if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
4281 const QQmlJSRegisterContent intermediate = from.storedIn(m_typeResolver->jsPrimitiveType());
4282 return conversion(intermediate, to, conversion(from, intermediate, variable));
4283 }
4284
4285 reject(u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
4286 .arg(from.descriptiveName(), to.descriptiveName()));
4287 return QString();
4288}
4289
4291{
4292 setError(u"Cannot generate efficient code for %1"_s.arg(thing));
4293}
4294
4296 : accumulatorOut(generator->m_state.accumulatorOut())
4297 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
4298 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
4300{
4301 if (accumulatorVariableOut.isEmpty())
4302 return;
4303
4304 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
4305 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
4306 const QQmlJSScope::ConstPtr stored = accumulatorOut.storedType();
4307 const QQmlJSScope::ConstPtr origStored = resolver->originalType(stored);
4308
4309 // If the stored type differs or if we store in QVariant and the contained type differs,
4310 // then we have to use a temporary ...
4311 if (!resolver->equals(origStored, stored)
4312 || (!resolver->equals(origContained, resolver->containedType(accumulatorOut))
4313 && resolver->equals(stored, resolver->varType()))) {
4314
4315 const bool storable = isTypeStorable(resolver, origStored);
4316 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
4317 generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut));
4318 generator->m_body += u"{\n"_s;
4319 if (storable) {
4320 generator->m_body += origStored->augmentedInternalName() + u' '
4321 + generator->m_state.accumulatorVariableOut + u";\n";
4322 }
4323 } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
4324 && generator->m_state.readsRegister(Accumulator)
4325 && resolver->registerIsStoredIn(
4326 generator->m_state.accumulatorOut(), resolver->varType())) {
4327 // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
4328 // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
4329 // move the input out of the way first.
4330 generator->m_state.accumulatorVariableIn
4331 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
4332 generator->m_body += u"{\n"_s;
4333 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
4334 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
4335 }
4336}
4337
4339{
4340 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
4341 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
4342 generator->m_state.accumulatorOut(), accumulatorOut,
4343 u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
4344 generator->m_body += u"}\n"_s;
4345 generator->m_state.setRegister(Accumulator, accumulatorOut);
4346 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
4347 } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
4348 generator->m_body += u"}\n"_s;
4349 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
4350 }
4351}
4352
4353
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
iterator end()
Definition qflatmap_p.h:773
const_iterator constBegin() const
Definition qflatmap_p.h:772
iterator find(const Key &key)
Definition qflatmap_p.h:816
const_iterator constEnd() const
Definition qflatmap_p.h:776
T value(const Key &key, const T &defaultValue) const
Definition qflatmap_p.h:636
const_iterator cbegin() const noexcept
Definition qhash.h:1214
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:927
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1219
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1291
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1215
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
const_iterator cend() const noexcept
Definition qhash.h:1218
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
void generate_Resume(int) override
void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override
QString consumedAccumulatorVariableIn() const
void generate_CmpNeInt(int lhs) override
void generate_LoadReg(int reg) override
bool shouldMoveRegister(int index) const
void generate_LoadZero() override
void generate_ShrConst(int value) override
QString convertStored(const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
void generateLookup(const QString &lookup, const QString &initialization, const QString &resultPreparation=QString())
void generate_LoadProperty(int nameIndex) override
QString contentType(const QQmlJSRegisterContent &content, const QString &var)
void generate_StoreReg(int reg) override
void generate_StoreLocal(int index) override
void generate_IteratorClose() override
void generate_GetLookup(int index) override
void generate_StoreNameSloppy(int nameIndex) override
void generate_Shl(int lhs) override
void generate_LoadName(int nameIndex) override
void generate_Exp(int lhs) override
void generate_MoveRegExp(int regExpId, int destReg) override
QString consumedRegisterVariable(int index) const
void generate_CmpInstanceOf(int lhs) override
void generate_JumpNoException(int offset) override
void generate_StoreScopedLocal(int scope, int index) override
void generate_CallName(int name, int argc, int argv) override
void generate_CmpStrictNotEqual(int lhs) override
void generate_ThrowOnNullOrUndefined() override
void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override
void generate_PopScriptContext() override
void generate_PopContext() override
QString convertContained(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
void generate_LoadQmlContextPropertyLookup(int index) override
QString conversion(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
void generate_LoadInt(int value) override
void generate_ThrowException() override
QString registerVariable(int index) const
QQmlJSRegisterContent registerType(int index) const
void generate_Jump(int offset) override
void generate_BitOr(int lhs) override
void generate_PushCatchContext(int index, int name) override
void generate_DestructureRestElement() override
QString compositeMetaType(const QString &elementName) const
void generate_Decrement() override
void generate_Increment() override
void generate_CmpEqNull() override
void generate_DeleteProperty(int base, int index) override
void generate_IteratorNext(int value, int offset) override
void generate_CmpGe(int lhs) override
void generate_CmpEq(int lhs) override
void generate_StoreProperty(int name, int baseReg) override
void generate_UnwindDispatch() override
void generate_LoadElement(int base) override
void generate_Sub(int lhs) override
void generate_GetIterator(int iterator) override
void generate_CmpLe(int lhs) override
void generate_Div(int lhs) override
void generate_LoadLocal(int index) override
void generate_CreateRestParameter(int argIndex) override
void generate_SetLookup(int index, int base) override
void generate_As(int lhs) override
void generate_ToObject() override
void generate_LoadTrue() override
void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override
void addInclude(const QString &include)
void generate_Mod(int lhs) override
QString metaTypeFromName(const QQmlJSScope::ConstPtr &type) const
QQmlJSAotFunction run(const Function *function, QQmlJS::DiagnosticMessage *error, bool basicBlocksValidationFailed)
void generate_StoreNameStrict(int name) override
void generate_CreateClass(int classIndex, int heritage, int computedNames) override
void generate_CallValue(int name, int argc, int argv) override
void generateEnumLookup(int index)
virtual QString metaType(const QQmlJSScope::ConstPtr &type)
void reject(const QString &thing)
QString setLookupPreparation(const QQmlJSRegisterContent &content, const QString &arg, int lookup)
void generate_BitAndConst(int rhs) override
void generate_LoadSuperConstructor() override
void generate_LoadUndefined() override
Verdict startInstruction(QV4::Moth::Instr::Type) override
void generate_CallPossiblyDirectEval(int argc, int argv) override
void generate_TypeofValue() override
void generate_LoadGlobalLookup(int index) override
void generate_TailCall(int func, int thisObject, int argc, int argv) override
void generate_MoveReg(int srcReg, int destReg) override
void generate_PushBlockContext(int index) override
void generate_ConvertThisToObject() override
void generate_CmpStrictEqual(int lhs) override
void generate_YieldStar() override
void generate_DeleteName(int name) override
QString compositeListMetaType(const QString &elementName) const
void generate_BitAnd(int lhs) override
void generate_IteratorNextForYieldStar(int iterator, int object, int offset) override
void generate_CmpNeNull() override
void generate_Construct(int func, int argc, int argv) override
void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override
void generate_UnwindToLabel(int level, int offset) override
void generate_CreateMappedArgumentsObject() override
QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext, const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger, BasicBlocks basicBlocks, InstructionAnnotations annotations)
void generate_LoadOptionalProperty(int name, int offset) override
void generate_JumpNotUndefined(int offset) override
void generate_GetException() override
void generate_ShlConst(int rhs) override
void generate_JumpFalse(int offset) override
QString getLookupPreparation(const QQmlJSRegisterContent &content, const QString &var, int lookup)
void generate_CmpLt(int lhs) override
void generate_GetTemplateObject(int index) override
void generate_StoreElement(int base, int index) override
void generate_CmpGt(int lhs) override
void generate_CreateUnmappedArgumentsObject() override
void generate_SetUnwindHandler(int offset) override
void generate_StoreSuperProperty(int property) override
void generate_DefineArray(int argc, int args) override
void generate_JumpTrue(int offset) override
void generate_CheckException() override
void generate_PushWithContext() override
void generate_CreateCallContext() override
QString changedRegisterVariable() const
QQmlJSRegisterContent lookupType(int lookupIndex) const
QString contentPointer(const QQmlJSRegisterContent &content, const QString &var)
void generate_UShrConst(int rhs) override
void generate_UShr(int lhs) override
void generate_ConstructWithSpread(int func, int argc, int argv) override
void generate_BitXorConst(int rhs) override
void generate_TypeofName(int name) override
void generate_LoadImport(int index) override
void generate_BitOrConst(int rhs) override
void generate_BitXor(int lhs) override
void generate_LoadConst(int index) override
void generate_CmpNe(int lhs) override
virtual QString metaObject(const QQmlJSScope::ConstPtr &objectType)
void generate_CallProperty(int name, int base, int argc, int argv) override
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override
void generate_CloneBlockContext() override
void generate_Mul(int lhs) override
void generate_LoadSuperProperty(int property) override
void generate_LoadFalse() override
void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override
void generate_SetException() override
void generate_LoadNull() override
void generate_DeadTemporalZoneCheck(int name) override
void generate_PushScriptContext(int index) override
void generate_CmpEqInt(int lhs) override
void generate_DeclareVar(int varName, int isDeletable) override
void generate_GetOptionalLookup(int index, int offset) override
void generate_Add(int lhs) override
void generate_CmpIn(int lhs) override
void generate_LoadRuntimeString(int stringId) override
void generate_Shr(int lhs) override
void endInstruction(QV4::Moth::Instr::Type) override
void generate_LoadScopedLocal(int scope, int index) override
void generate_CallGlobalLookup(int index, int argc, int argv) override
QString metaTypeFromType(const QQmlJSScope::ConstPtr &type) const
void generate_LoadClosure(int value) override
QString lookupVariable(int lookupIndex) const
void generate_MoveConst(int constIndex, int destTemp) override
const QV4::Compiler::JSUnitGenerator * m_jsUnitGenerator
bool isArgument(int registerIndex) const
static bool instructionManipulatesContext(QV4::Moth::Instr::Type type)
const Function * m_function
const QQmlJSTypeResolver * m_typeResolver
QQmlJS::DiagnosticMessage * m_error
InstructionAnnotations m_annotations
State initialState(const Function *function)
State nextStateFromAnnotations(const State &oldState, const InstructionAnnotations &annotations)
void setError(const QString &message, int instructionOffset)
QQmlJSRegisterContent argumentType(int registerIndex) const
QQmlJSRegisterContent storedIn(const QQmlJSScope::ConstPtr &newStoredType) const
QQmlJSScope::ConstPtr scopeType() const
ContentVariant variant() const
QQmlJSScope::ConstPtr type() const
QQmlJSScope::ConstPtr storedType() const
QQmlJSMetaEnum enumeration() const
bool isComposite() const
AnnotatedScope extensionType() const
ScopeType scopeType() const
QString internalName() const
bool isReferenceType() const
AccessSemantics accessSemantics() const
bool isListProperty() const
static QQmlJSScope::ConstPtr nonCompositeBaseType(const QQmlJSScope::ConstPtr &type)
QString augmentedInternalName() const
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr valueType() const
bool equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
QQmlJSRegisterContent original(const QQmlJSRegisterContent &type) const
QQmlJSRegisterContent merge(const QQmlJSRegisterContent &a, const QQmlJSRegisterContent &b) const
QQmlJSScope::ConstPtr dateTimeType() const
QQmlJSMetaMethod selectConstructor(const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &argument, bool *isExtension) const
QQmlJSScope::ConstPtr int64Type() const
QQmlJSScope::ConstPtr stringType() const
QQmlJSScope::ConstPtr listPropertyType() const
QQmlJSScope::ConstPtr nullType() const
QQmlJSScope::ConstPtr timeType() const
QQmlJSScope::ConstPtr mathObject() const
QQmlJSScope::ConstPtr genericType(const QQmlJSScope::ConstPtr &type, ComponentIsGeneric allowComponent=ComponentIsGeneric::No) const
QQmlJSScope::ConstPtr uint64Type() const
bool areEquivalentLists(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
bool registerIsStoredIn(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
bool canPopulate(const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &argument, bool *isExtension) const
QQmlJSScope::ConstPtr containedType(const QQmlJSRegisterContent &container) const
QQmlJSScope::ConstPtr jsPrimitiveType() const
bool isNumeric(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr uint8Type() const
QQmlJSScope::ConstPtr byteArrayType() const
QQmlJSScope::ConstPtr consoleObject() const
QQmlJSScope::ConstPtr boolType() const
QQmlJSScope::ConstPtr qObjectListType() const
QQmlJSScope::ConstPtr jsGlobalObject() const
bool registerContains(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
QString nameForType(const QQmlJSScope::ConstPtr &type) const
bool inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
bool isIntegral(const QQmlJSRegisterContent &type) const
bool canHold(const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
bool isPrimitive(const QQmlJSRegisterContent &type) const
QQmlJSScope::ConstPtr int16Type() const
QQmlJSRegisterContent builtinType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr jsValueType() const
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr originalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr sizeType() const
QQmlJSRegisterContent valueType(const QQmlJSRegisterContent &list) const
QQmlJSScope::ConstPtr originalContainedType(const QQmlJSRegisterContent &container) const
QQmlJSScope::ConstPtr uint16Type() const
QQmlJSScope::ConstPtr comparableType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr extractNonVoidFromOptionalType(const QQmlJSRegisterContent &content) const
QQmlJSScope::ConstPtr int8Type() const
bool isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr int32Type() const
QQmlJSScope::ConstPtr variantListType() const
QQmlJSScope::ConstPtr voidType() const
QQmlJSScope::ConstPtr forOfIteratorPtr() const
QQmlJSScope::ConstPtr metaObjectType() const
QQmlJSScope::ConstPtr forInIteratorPtr() const
QQmlJSScope::ConstPtr realType() const
bool isOptionalType(const QQmlJSRegisterContent &content) const
QQmlJSScope::ConstPtr urlType() const
bool isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr dateType() const
QQmlJSScope::ConstPtr typeForConst(QV4::ReturnedValue rv) const
bool isSignedInteger(const QQmlJSScope::ConstPtr &type) const
QQmlJSRegisterContent scopedType(const QQmlJSScope::ConstPtr &scope, const QString &name, int lookupIndex=QQmlJSRegisterContent::InvalidLookupIndex, QQmlJSScopesByIdOptions options=Default) const
bool isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr qObjectType() const
QQmlJSScope::ConstPtr varType() const
QQmlJSScope::ConstPtr variantMapType() const
QQmlJSScope::ConstPtr floatType() const
iterator begin()
Definition qset.h:136
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
int absoluteOffset(int relativeOffset) const
QSet< QString >::iterator it
Combined button and popup list for selecting options.
quint64 ReturnedValue
#define QT_WARNING_POP
#define Q_FUNC_INFO
#define QT_WARNING_PUSH
#define QT_WARNING_DISABLE_CLANG(text)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qFpClassify(qfloat16 f) noexcept
Definition qfloat16.h:286
return ret
GLenum GLuint GLint level
GLuint64 key
GLuint index
[2]
GLuint GLuint end
GLsizei GLenum GLenum * types
GLenum GLenum GLsizei count
GLenum type
GLenum access
GLenum target
GLenum GLuint GLintptr offset
GLuint name
GLsizei GLsizei GLchar * source
GLenum func
Definition qopenglext.h:663
GLuint in
GLuint64EXT * result
[6]
GLenum GLenum variable
GLenum GLenum GLenum input
GLboolean invert
Definition qopenglext.h:226
static int log2(uint i)
static QString toNumericString(double value)
static QString messageTypeForMethod(const QString &method)
#define BYTECODE_UNIMPLEMENTED()
#define INJECT_TRACE_INFO(function)
static QString minExpression(int argc)
static QString maxExpression(int argc)
static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
static bool isStrict(const QmlIR::Document *doc)
static QString internalName(const QQmlJSScope::ConstPtr &scope)
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType, const QQmlJSScope::ConstPtr &rhsType)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
#define zero
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3517
static const uint base
Definition qurlidna.cpp:20
#define decode(x)
static int sign(int x)
const char property[13]
Definition qwizard.cpp:101
QRandomGenerator generator(sseq)
obj metaObject() -> className()
QVariant variant
[1]
int originalValue
[1]
QDBusArgument argument
QJSValueList args
static QByteArray normalizedType(const char *type)
AccumulatorConverter(QQmlJSCodeGenerator *generator)
QList< QQmlJSRegisterContent > argumentTypes
QQmlJSScope::ConstPtr qmlScope
QQmlJSRegisterContent returnType
const QQmlJSRegisterContent & accumulatorIn() const
The accumulatorIn is the input register of the current instruction.
bool canMoveReadRegister(int registerIndex) const
bool isRegisterAffectedBySideEffects(int registerIndex) const
QQmlJSRegisterContent readRegister(int registerIndex) const
const QQmlJSRegisterContent & changedRegister() const
QQmlJSRegisterContent readAccumulator() const
The readAccumulator is the register content expected by the current instruction.
const QQmlJSRegisterContent & accumulatorOut() const
The accumulatorOut is the output register of the current instruction.
QQmlJSScope::ConstPtr scope
static QString toLiteral(const QString &s, QStringView ctor=u"QStringLiteral")
static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
std::vector< unsigned > labelInfo
QString jsClassMember(int jsClassId, int member) const
QString lookupName(int index) const
ReturnedValue constant(int idx) const
int lookupNameIndex(int index) const
QString stringForIndex(int index) const
int jsClassSize(int jsClassId) const
static constexpr StaticValue fromReturnedValue(ReturnedValue val)