Qt 6.x
The Qt SDK
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)
59 : QQmlJSCompilePass(unitGenerator, typeResolver, logger)
60 , m_context(compilerContext)
61{}
62
64{
65 return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
66}
67
69{
70 return u"[]() { static const auto t = QMetaType::fromName(\""_s
71 + QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
72 + u"\"); return t; }()"_s;
73}
74
76{
77 if (!objectType->isComposite()) {
78 if (objectType->internalName() == u"QObject"_s
79 || objectType->internalName() == u"QQmlComponent"_s) {
80 return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
81 }
82 return metaTypeFromName(objectType) + u".metaObject()"_s;
83 }
84
85 reject(u"retrieving the metaObject of a composite type without using an instance."_s);
86 return QString();
87}
88
90{
94}
95
97 const Function *function, const InstructionAnnotations *annotations,
99{
100 m_annotations = annotations;
102 m_error = error;
103
105
106 auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) {
107 // Don't generate any variables for registers that are initialized with undefined.
108 if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType))
109 return;
110
111 auto &typesForRegisters = m_registerVariables[registerIndex];
112 if (!typesForRegisters.contains(seenType)) {
113 auto &currentRegisterNames = registerNames[registerIndex];
114 QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)];
115 if (name.isEmpty())
116 name = u"r%1_%2"_s.arg(registerIndex).arg(currentRegisterNames.size());
117 typesForRegisters[seenType] = name;
118 }
119 };
120
122QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
123 for (const auto &annotation : *m_annotations) {
124 addVariable(annotation.second.changedRegisterIndex,
125 annotation.second.changedRegister.storedType());
126 for (auto it = annotation.second.typeConversions.begin(),
127 end = annotation.second.typeConversions.end();
128 it != end; ++it) {
129 addVariable(it.key(), it.value().content.storedType());
130 }
131 }
133
134 // ensure we have m_labels for loops
135 for (const auto loopLabel : m_context->labelInfo)
136 m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
137
138 // Initialize the first instruction's state to hold the arguments.
139 // After this, the arguments (or whatever becomes of them) are carried
140 // over into any further basic blocks automatically.
141 m_state.State::operator=(initialState(m_function));
142
143 const QByteArray byteCode = function->code;
144 decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
145
147 result.includes.swap(m_includes);
148
149 result.code += u"// %1 at line %2, column %3\n"_s
150 .arg(m_context->name).arg(m_context->line).arg(m_context->column);
151
152 QDuplicateTracker<QString> generatedVariables;
153 for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
154 registerIt != registerEnd; ++registerIt) {
155 const auto &registerTypes = *registerIt;
156 const int registerIndex = registerIt.key();
157
158 const bool registerIsArgument = isArgument(registerIndex);
159
160 for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd();
161 registerTypeIt != end; ++registerTypeIt) {
162
163 const QQmlJSScope::ConstPtr storedType = registerTypeIt.key();
164
165 if (generatedVariables.hasSeen(registerTypeIt.value()))
166 continue;
167
168 result.code += storedType->internalName();
169
170 const bool isPointer
171 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
172 if (isPointer)
173 result.code += u" *"_s;
174 else
175 result.code += u' ';
176
177 if (!registerIsArgument
178 && registerIndex != Accumulator
179 && registerIndex != This
181 function->registerTypes[registerIndex - firstRegisterIndex()],
183 result.code += registerTypeIt.value() + u" = "_s;
184 result.code += convertStored(m_typeResolver->voidType(), storedType, QString());
185 } else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
186 argumentType(registerIndex), storedType)) {
187 const int argumentIndex = registerIndex - FirstArgument;
189 = m_function->argumentTypes[argumentIndex];
190 const QQmlJSRegisterContent original
192
193 const bool needsConversion = argument != original;
194 if (!isPointer && registerTypes.size() == 1 && !needsConversion) {
195 // Not a pointer, never written to, and doesn't need any initial conversion.
196 // This is a readonly argument.
197 //
198 // We would like to make the variable a const ref if it's a readonly argument,
199 // but due to the various call interfaces accepting non-const values, we can't.
200 // We rely on those calls to still not modify their arguments in place.
201 result.code += u'&';
202 }
203
204 result.code += registerTypeIt.value() + u" = "_s;
205
206 const QString originalValue = u"*static_cast<"_s + castTargetName(original.storedType())
207 + u"*>(argumentsPtr["_s + QString::number(argumentIndex) + u"])"_s;
208
209 if (needsConversion)
210 result.code += conversion(original, argument, originalValue);
211 else
212 result.code += originalValue;
213 } else {
214 result.code += registerTypeIt.value();
215 }
216 result.code += u";\n"_s;
217 }
218 }
219
220 result.code += m_body;
221
222 for (const QQmlJSRegisterContent &argType : std::as_const(function->argumentTypes)) {
223 if (argType.isValid()) {
224 result.argumentTypes.append(
225 m_typeResolver->originalType(argType.storedType())
227 } else {
228 result.argumentTypes.append(u"void"_s);
229 }
230 }
231
232 if (function->returnType) {
233 result.returnType = function->returnType->internalName();
234 if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
235 result.returnType += u'*';
236 } else {
237 result.returnType = u"void"_s;
238 }
239
240 return result;
241}
242
244{
245 if (auto ret = m_function->returnType) {
246 return ret->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
248 : ret->internalName() + u"()"_s;
249 }
250 return QString();
251}
252
254{
256
257 if (m_function->returnType) {
258 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
263 m_body += u"if (!"_s + in + u".isValid())\n"_s;
264 m_body += u" "_s + signalUndefined;
267 m_body += u"if ("_s + in
268 + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
269 m_body += u" "_s + signalUndefined;
272 m_body += u"if ("_s + in + u".isUndefined())\n"_s;
273 m_body += u" "_s + signalUndefined;
274 }
275 m_body += u"return "_s
277 } else {
280 m_body += signalUndefined;
281 }
282 m_body += u"return "_s + convertStored(
284 }
285 } else {
286 m_body += u"return"_s;
287 }
288
289 m_body += u";\n"_s;
290 m_skipUntilNextLabel = true;
291 resetState();
292}
293
295{
297}
298
300{
301 if (value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max()) {
302 const int i = value;
303 if (i == value)
304 return QString::number(i);
305 }
306
307 switch (qFpClassify(value)) {
308 case FP_INFINITE: {
309 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
310 return std::signbit(value) ? (u'-' + inf) : inf;
311 }
312 case FP_NAN:
313 return u"std::numeric_limits<double>::quiet_NaN()"_s;
314 case FP_ZERO:
315 return std::signbit(value) ? u"-0.0"_s : u"0"_s;
316 default:
317 break;
318 }
319
320 return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
321}
322
324{
326
327 // You cannot actually get it to generate LoadConst for anything but double. We have
328 // a numer of specialized instructions for the other types, after all. However, let's
329 // play it safe.
330
331 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
334
336 if (type == m_typeResolver->realType()) {
339 toNumericString(value.doubleValue()));
340 } else if (type == m_typeResolver->int32Type()) {
343 QString::number(value.integerValue()));
344 } else if (type == m_typeResolver->boolType()) {
347 value.booleanValue() ? u"true"_s : u"false"_s);
348 } else if (type == m_typeResolver->voidType()) {
351 QString());
352 } else if (type == m_typeResolver->nullType()) {
355 u"nullptr"_s);
356 } else {
357 reject(u"unsupported constant type"_s);
358 }
359
360 m_body += u";\n"_s;
361}
362
364{
366
368 m_body += u" = "_s + conversion(
370 m_body += u";\n"_s;
371}
372
374{
376
378 m_body += u" = "_s + conversion(
380 m_body += u";\n"_s;
381}
382
384{
386
388 m_body += u" = "_s + conversion(
390 m_body += u";\n"_s;
391}
392
394{
396
399 u"nullptr"_s);
400 m_body += u";\n"_s;
401}
402
404{
406
409 QString());
410 m_body += u";\n"_s;
411}
412
414{
416
418 m_body += u" = "_s;
421 m_body += u";\n"_s;
422}
423
424void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
425{
427
429
431 if (var.isEmpty())
432 return; // Do not load 'undefined'
433
434 const auto v4Value = QV4::StaticValue::fromReturnedValue(
435 m_jsUnitGenerator->constant(constIndex));
436
437 const auto changed = m_state.changedRegister();
438 QQmlJSScope::ConstPtr contained;
440
441 m_body += var + u" = "_s;
442 if (v4Value.isNull()) {
443 contained = m_typeResolver->nullType();
444 } else if (v4Value.isUndefined()) {
445 contained = m_typeResolver->voidType();
446 } else if (v4Value.isBoolean()) {
447 contained = m_typeResolver->boolType();
448 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
449 } else if (v4Value.isInteger()) {
450 contained = m_typeResolver->int32Type();
451 input = QString::number(v4Value.int_32());
452 } else if (v4Value.isDouble()) {
453 contained = m_typeResolver->realType();
454 input = toNumericString(v4Value.doubleValue());
455 } else {
456 reject(u"unknown const type"_s);
457 }
458 m_body += conversion(contained, changed, input) + u";\n"_s;
459}
460
462{
464
466 m_body += u" = "_s;
469 m_body += u";\n"_s;
470}
471
473{
475
479 if (var.isEmpty())
480 return; // don't store "undefined"
481 m_body += var;
482 m_body += u" = "_s;
485 m_body += u";\n"_s;
486}
487
488void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
489{
491
493 const QString destRegName = changedRegisterVariable();
494 if (destRegName.isEmpty())
495 return; // don't store things we cannot store.
496 m_body += destRegName;
497 m_body += u" = "_s;
500 m_body += u";\n"_s;
501}
502
504{
507}
508
510{
512 reject(u"LoadLocal"_s);
513}
514
516{
519}
520
522{
523 Q_UNUSED(scope)
526}
527
529{
530 Q_UNUSED(scope)
533}
534
536{
538
540 m_body += u" = "_s;
543 m_body += u";\n"_s;
544}
545
546void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
547{
548 Q_UNUSED(regExpId)
549 Q_UNUSED(destReg)
551}
552
554{
556 reject(u"LoadClosure"_s);
557}
558
560{
561 Q_UNUSED(nameIndex)
562 reject(u"LoadName"_s);
563}
564
566{
568
569 AccumulatorConverter registers(this);
570
571 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index)
572 + u", &"_s + m_state.accumulatorVariableOut + u", "_s
574 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
575 + QString::number(index) + u')';
576 generateLookup(lookup, initialization);
577}
578
580{
582
583 AccumulatorConverter registers(this);
584
585 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
586 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
589 + conversion(
591 u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
592 + u";\n"_s;
593 return;
594 }
595
596 const QString indexString = QString::number(index);
598 const QString lookup = u"aotContext->loadContextIdLookup("_s
599 + indexString + u", "_s
601 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
602 + indexString + u')';
603 generateLookup(lookup, initialization);
604 return;
605 }
606
607 const bool isProperty = m_state.accumulatorOut().isProperty();
610 if (isProperty) {
612
613 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
614 + indexString + u", "_s
616 const QString initialization
617 = u"aotContext->initLoadScopeObjectPropertyLookup("_s
618 + indexString + u", "_s
619 + lookupType + u')';
620 const QString preparation = getLookupPreparation(
622
623 generateLookup(lookup, initialization, preparation);
625 generateTypeLookup(index);
626 } else {
627 reject(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
628 }
629}
630
632{
634
635 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
637 Q_ASSERT(type.isProperty());
638
639 switch (type.variant()) {
642 // Do not convert here. We may intentionally pass the "wrong" type, for example to trigger
643 // a property reset.
644 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
645 + u", "_s
647 + u", "_s
649 m_body += u";\n"_s;
650 break;
651 }
654 reject(u"assignment to scope method"_s);
655 break;
656 default:
657 Q_UNREACHABLE();
658 }
659}
660
662{
665}
666
668{
670
671 const QQmlJSRegisterContent baseType = registerType(base);
672
674 || (!baseType.isList()
676 reject(u"LoadElement with non-list base type or non-numeric arguments"_s);
677 return;
678 }
679
680 AccumulatorConverter registers(this);
681
682 const QString baseName = registerVariable(base);
683 const QString indexName = m_state.accumulatorVariableIn;
684
685 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
687 m_state.accumulatorOut(), QString()) + u";\n"_s;
688
690 m_body += u"if (!QJSNumberCoercion::isInteger("_s + indexName + u"))\n"_s
691 + voidAssignment
692 + u"else "_s;
693 }
694
697 m_body += u"if ("_s + indexName + u" < 0)\n"_s
698 + voidAssignment
699 + u"else "_s;
700 }
701
703 // Our QQmlListProperty only keeps plain QObject*.
704 const auto valueType = m_typeResolver->valueType(baseType);
705 const auto elementType = m_typeResolver->globalType(
707
708 m_body += u"if ("_s + indexName + u" < "_s + baseName
709 + u".count(&"_s + baseName + u"))\n"_s;
710 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
711 conversion(elementType, m_state.accumulatorOut(),
712 baseName + u".at(&"_s + baseName + u", "_s
713 + indexName + u')') + u";\n"_s;
714 m_body += u"else\n"_s
715 + voidAssignment;
716 return;
717 }
718
719 const auto elementType = m_typeResolver->valueType(baseType);
720
721 QString access = baseName + u".at("_s + indexName + u')';
722
723 // TODO: Once we get a char type in QML, use it here.
725 access = u"QString("_s + access + u")"_s;
727 reject(u"LoadElement on a sequence potentially affected by side effects"_s);
728 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
729 reject(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
730
731 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
732 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
733 conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
734 m_body += u"else\n"_s
735 + voidAssignment;
736}
737
739{
741
742 const QQmlJSRegisterContent baseType = registerType(base);
743 const QQmlJSRegisterContent indexType = registerType(index);
744
745 if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) {
746 reject(u"StoreElement with non-list base type or non-numeric arguments"_s);
747 return;
748 }
749
751 reject(u"indirect StoreElement"_s);
752 return;
753 }
754
755 const QString baseName = registerVariable(base);
756 const QString indexName = registerVariable(index);
757
758 const auto valueType = m_typeResolver->valueType(baseType);
759 const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType(
760 m_typeResolver->containedType(valueType)));
761
762 m_body += u"if ("_s;
763 if (!m_typeResolver->isIntegral(indexType))
764 m_body += u"QJSNumberCoercion::isInteger("_s + indexName + u") && "_s;
766 m_body += indexName + u" >= 0 && "_s;
767 m_body += indexName + u" < "_s + baseName + u".count(&"_s + baseName
768 + u"))\n"_s;
769 m_body += u" "_s + baseName + u".replace(&"_s + baseName
770 + u", "_s + indexName + u", "_s;
772 + u");\n"_s;
773}
774
776{
777 Q_UNUSED(nameIndex)
778 reject(u"LoadProperty"_s);
779}
780
782{
786}
787
789{
790 const QString enumMember = m_state.accumulatorOut().enumMember();
791
792 // If we're referring to the type, there's nothing to do.
793 if (enumMember.isEmpty())
794 return;
795
796 // If the metaenum has the value, just use it and skip all the rest.
798 if (metaEnum.hasValues()) {
800 + QString::number(metaEnum.value(enumMember));
801 m_body += u";\n"_s;
802 return;
803 }
804
806
807 // Otherwise we would have found an enum with values.
808 Q_ASSERT(!scopeType->isComposite());
809
810 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
811 if (enumName.isEmpty()) {
812 if (metaEnum.isFlag() && !metaEnum.name().isEmpty())
813 reject(u"qmltypes misses name entry for flag; did you pass the enum type to Q_FLAG instead of the QFlag type?"
814 "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
815 reject(u"qmltypes misses name entry for enum"_s);
816 }
817 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
818 + u", &"_s + m_state.accumulatorVariableOut + u')';
819 const QString initialization = u"aotContext->initGetEnumLookup("_s
820 + QString::number(index) + u", "_s + metaObject(scopeType)
821 + u", \""_s + enumName + u"\", \""_s + enumMember
822 + u"\")"_s;
823 generateLookup(lookup, initialization);
824}
825
826void QQmlJSCodeGenerator::generateTypeLookup(int index)
827{
828 const QString indexString = QString::number(index);
830 const QString namespaceString
831 = accumulatorIn.isImportNamespace()
832 ? QString::number(accumulatorIn.importNamespace())
833 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
834
835 switch (m_state.accumulatorOut().variant()) {
837 rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
838 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
839 + u", &"_s + m_state.accumulatorVariableOut + u')';
840 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
841 + u", "_s + namespaceString + u')';
842 generateLookup(lookup, initialization);
843 break;
844 }
846 break;
848 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
849 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
850 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
851 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
852 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
853 generateLookup(lookup, initialization);
854 break;
855 }
857 reject(u"script lookup"_s);
858 break;
862 // TODO: Can we trigger this somehow?
863 // It might be impossible, but we better be safe here.
864 reject(u"meta-object stored in different type"_s);
865 }
866 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
867 + u", &"_s + m_state.accumulatorVariableOut + u')';
868 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
869 + u", "_s + namespaceString + u")"_s;
870 generateLookup(lookup, initialization);
871 break;
872 }
873 default:
874 Q_UNREACHABLE();
875 }
876}
877
878void QQmlJSCodeGenerator::generateVariantEqualityComparison(
879 const QQmlJSRegisterContent &nonStorableContent, const QString &registerName, bool invert)
880{
881 const auto nonStorableType = m_typeResolver->containedType(nonStorableContent);
882 QQmlJSScope::ConstPtr comparedType =
883 m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())
886
887 // The common operations for both nulltype and voidtype
888 m_body += u"if ("_s + registerName
889 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
892 u"static_cast<const QJSPrimitiveValue *>("_s + registerName
893 + u".constData())"_s + u"->type() "_s
894 + (invert ? u"!="_s : u"=="_s)
895 + (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
896 ? u"QJSPrimitiveValue::Null"_s
897 : u"QJSPrimitiveValue::Undefined"_s))
898 + u";\n} else if ("_s + registerName
899 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
900 + m_state.accumulatorVariableOut + u" = "_s
901 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
902 (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
903 + registerName + u".constData())"_s + u"->"_s
904 + (m_typeResolver->equals(comparedType, m_typeResolver->nullType())
905 ? u"isNull()"_s
906 : u"isUndefined()"_s))
907 + u";\n}"_s;
908
909 // Generate nullType specific operations (the case when variant contains QObject * or
910 // std::nullptr_t)
911 if (m_typeResolver->equals(nonStorableType, m_typeResolver->nullType())) {
912 m_body += u"else if ("_s + registerName
913 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
916 u"*static_cast<QObject *const *>("_s + registerName
917 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
918 + u" nullptr"_s)
919 + u";\n} else if ("_s + registerName
920 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
921 + m_state.accumulatorVariableOut + u" = "_s
922 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
923 (invert ? u"false"_s : u"true"_s))
924 + u";\n}\n"_s;
925 }
926
927 // fallback case (if variant contains a different type, then it is not null or undefined)
928 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
930 (invert ? (registerName + u".isValid() ? true : false"_s)
931 : (registerName + u".isValid() ? false : true"_s)))
932 + u";\n}"_s;
933}
934
935void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
936{
938 != QQmlJSScope::AccessSemantics::Reference) {
939 reject(error);
940 }
941}
942
950bool QQmlJSCodeGenerator::generateContentPointerCheck(
951 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
952 const QString &variable, const QString &errorMessage)
953{
954 const QQmlJSScope::ConstPtr scope = required;
957 [&](const QQmlJSScope::ConstPtr &base) {
958 return m_typeResolver->equals(base, scope);
959 })) {
960 return false;
961 }
962
963 if (!m_typeResolver->canHold(input, scope)) {
964 reject(u"lookup of members of %1 in %2"_s.arg(
965 scope->internalName(), input->internalName()));
966 }
967
968 bool needsVarContentConversion = false;
969 QString processedErrorMessage;
970 if (actual.storedType()->isReferenceType()) {
971 // Since we have verified the type in qqmljstypepropagator.cpp we now know
972 // that we can only have either null or the actual type here. Therefore,
973 // it's enough to check the pointer for null.
974 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
975 processedErrorMessage = errorMessage.arg(u"null");
976 } else if (m_typeResolver->equals(actual.storedType(), m_typeResolver->varType())) {
977 // Since we have verified the type in qqmljstypepropagator.cpp we now know
978 // that we can only have either undefined or the actual type here. Therefore,
979 // it's enough to check the QVariant for isValid().
980 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
981 needsVarContentConversion = true;
982 processedErrorMessage = errorMessage.arg(u"undefined");
983 } else {
984 reject(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
985 }
986
988 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
989 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
990 m_body += u" return "_s + errorReturnValue() + u";\n"_s;
991 m_body += u"}\n"_s;
992 return needsVarContentConversion;
993}
994
995QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
996 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
997 const QString &variable, const QString &errorMessage)
998{
999 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1000 return variable + u".data()"_s;
1001 return contentPointer(actual, variable);
1002}
1003
1004QString QQmlJSCodeGenerator::resolveQObjectPointer(
1005 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1006 const QString &variable, const QString &errorMessage)
1007{
1008 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1009 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1010 return variable;
1011}
1012
1014{
1016
1018 reject(u"lookup of function property."_s);
1019 return;
1020 }
1021
1024 // If we have an object module prefix, we need to pass through the original object.
1029 + u";\n"_s;
1030 }
1031 return;
1032 }
1033
1034 AccumulatorConverter registers(this);
1035
1038 return;
1039 }
1040
1041 const QString indexString = QString::number(index);
1042 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1044 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1045 const auto accumulatorIn = m_state.accumulatorIn();
1047 const bool isReferenceType = scope->isReferenceType();
1048
1049 switch (m_state.accumulatorOut().variant()) {
1051 if (!isReferenceType) {
1052 // This can happen on incomplete type information. We contextually know that the
1053 // type must be a QObject, but we cannot construct the inheritance chain. Then we
1054 // store it in a generic type. Technically we could even convert it to QObject*, but
1055 // that would be expensive.
1056 reject(u"attached object for non-QObject type"_s);
1057 }
1058
1060 // This can happen if we retroactively determine that the property might not be
1061 // what we think it is (ie, it can be shadowed).
1062 reject(u"attached object of potentially non-QObject base"_s);
1063 }
1064
1065 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1066
1067 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1068 + u", "_s + m_state.accumulatorVariableIn
1069 + u", &"_s + m_state.accumulatorVariableOut + u')';
1070 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1071 + indexString + u", "_s + namespaceString + u", "_s
1073 generateLookup(lookup, initialization);
1074 return;
1075 }
1079 generateTypeLookup(index);
1080 return;
1081 }
1082 default:
1083 break;
1084 }
1085
1087
1089 reject(u"lookup in QJSValue"_s);
1090 } else if (isReferenceType) {
1091 const QString inputPointer = resolveQObjectPointer(
1092 scope, accumulatorIn, m_state.accumulatorVariableIn,
1093 u"Cannot read property '%1' of %2"_s.arg(
1095 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1096 + u", "_s + inputPointer + u", "_s
1098 const QString initialization = u"aotContext->initGetObjectLookup("_s
1099 + indexString + u", "_s + inputPointer
1101 + u')';
1102 const QString preparation = getLookupPreparation(
1104 generateLookup(lookup, initialization, preparation);
1105 } else if ((accumulatorIn.isList()
1107 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1108 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1109 if (stored->isListProperty()) {
1111 m_body += conversion(
1114 m_state.accumulatorVariableIn + u".count("_s + u'&'
1116 m_body += u";\n"_s;
1117 } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1122 m_state.accumulatorVariableIn + u".length()"_s)
1123 + u";\n"_s;
1124 } else {
1125 reject(u"access to 'length' property of sequence wrapped in non-sequence"_s);
1126 }
1127 } else if (m_typeResolver->registerIsStoredIn(accumulatorIn,
1129 QString mapLookup = m_state.accumulatorVariableIn + u"["_s
1133 m_state.accumulatorOut(), mapLookup);
1134 m_body += u";\n"_s;
1135 } else {
1137 reject(u"reading from a value that's potentially affected by side effects"_s);
1138
1139 const QString inputContentPointer = resolveValueTypeContentPointer(
1140 scope, accumulatorIn, m_state.accumulatorVariableIn,
1141 u"Cannot read property '%1' of %2"_s.arg(
1143
1144 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1145 + u", "_s + inputContentPointer
1147 + u')';
1148 const QString initialization = u"aotContext->initGetValueLookup("_s
1149 + indexString + u", "_s
1150 + metaObject(scope) + u", "_s
1152 const QString preparation = getLookupPreparation(
1154 generateLookup(lookup, initialization, preparation);
1155 }
1156}
1157
1159{
1163}
1164
1165void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
1166{
1167 Q_UNUSED(nameIndex)
1168 Q_UNUSED(baseReg)
1169 reject(u"StoreProperty"_s);
1170}
1171
1173 const QQmlJSRegisterContent &content, const QString &arg, int lookup)
1174{
1175 if (m_typeResolver->registerContains(content, content.storedType()))
1176 return QString();
1177
1179 return u"const QMetaType argType = aotContext->lookupResultMetaType("_s
1180 + QString::number(lookup) + u");\n"_s
1181 + u"if (argType.isValid())\n "_s + arg + u".convert(argType)";
1182 }
1183 // TODO: We could make sure they're compatible, for example QObject pointers.
1184 return QString();
1185}
1186
1187
1189{
1191
1192 const QString indexString = QString::number(index);
1194 const QQmlJSRegisterContent callBase = m_typeResolver->original(registerType(baseReg));
1196 callBase, m_jsUnitGenerator->lookupName(index));
1197
1198 if (specific.storedType().isNull()) {
1199 reject(u"SetLookup. Could not find property "
1201 + u" on type "
1202 + callBase.storedType()->internalName());
1203 return;
1204 }
1205
1206 // Choose a container that can hold both, the "in" accumulator and what we actually want.
1207 // If the types are all the same because we can all store them as verbatim C++ types,
1208 // the container will also be that type.
1209 const QQmlJSScope::ConstPtr container = m_typeResolver->merge(specific.storedType(), valueType);
1210 const QQmlJSRegisterContent property = specific.storedIn(container);
1211
1212 const QString object = registerVariable(baseReg);
1213 m_body += u"{\n"_s;
1214 QString variableIn;
1215 QString variableInType;
1216 QString preparation;
1217 QString argType;
1220 m_body += u"auto converted = "_s
1222 + u";\n"_s;
1223 variableIn = contentPointer(property, u"converted"_s);
1224 variableInType = contentType(property, u"converted"_s);
1225 preparation = setLookupPreparation(property, u"converted"_s, index);
1226 if (preparation.isEmpty())
1227 argType = contentType(property, u"converted"_s);
1228 else
1229 argType = u"argType"_s;
1230 } else {
1233 argType = variableInType;
1234 }
1235
1236 switch (property.scopeType()->accessSemantics()) {
1237 case QQmlJSScope::AccessSemantics::Reference: {
1238 const QString basePointer = resolveQObjectPointer(
1239 property.scopeType(), registerType(baseReg), object,
1240 u"TypeError: Value is %1 and could not be converted to an object"_s);
1241
1242 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1243 + u", "_s + basePointer + u", "_s + variableIn + u')';
1244 const QString initialization = u"aotContext->initSetObjectLookup("_s
1245 + indexString + u", "_s + basePointer + u", "_s + argType + u')';
1246 generateLookup(lookup, initialization, preparation);
1247 break;
1248 }
1249 case QQmlJSScope::AccessSemantics::Sequence: {
1250 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1251 if (propertyName != u"length"_s) {
1252 reject(u"setting non-length property on a sequence type"_s);
1253 break;
1254 }
1255
1256 if (!callBase.storedType()->isListProperty()) {
1257 reject(u"resizing sequence types (because of missing write-back)"_s);
1258 break;
1259 }
1260
1261 // We can resize without write back on a list property because it's actually a reference.
1262 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1263 m_body += u"const int end = "_s
1264 + (variableIn.startsWith(u'&') ? variableIn.mid(1) : (u'*' + variableIn))
1265 + u";\n"_s;
1266 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1267 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1268 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1269 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1270 + u";\n"_s;
1271 break;
1272 }
1273 case QQmlJSScope::AccessSemantics::Value: {
1274 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1275 Q_ASSERT(specific.isProperty());
1276 const QQmlJSRegisterContent property = specific.storedIn(
1278
1279 const QString baseContentPointer = resolveValueTypeContentPointer(
1280 property.scopeType(), registerType(baseReg), object,
1281 u"TypeError: Value is %1 and could not be converted to an object"_s);
1282
1283 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1284 + u", "_s + baseContentPointer
1285 + u", "_s + variableIn + u')';
1286 const QString initialization = u"aotContext->initSetValueLookup("_s
1287 + indexString + u", "_s + metaObject(property.scopeType())
1288 + u", "_s + argType + u')';
1289
1290 generateLookup(lookup, initialization, preparation);
1291 reject(u"SetLookup on value types (because of missing write-back)"_s);
1292 break;
1293 }
1294 case QQmlJSScope::AccessSemantics::None:
1295 Q_UNREACHABLE();
1296 break;
1297 }
1298
1299 m_body += u"}\n"_s;
1300}
1301
1303{
1306}
1307
1309{
1312}
1313
1315{
1317}
1318
1320{
1322}
1323
1325{
1327}
1328
1329QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
1330{
1331 QString types;
1332 QString args;
1333
1337 types = u"QMetaType()"_s;
1338 args = u"nullptr"_s;
1339 } else {
1340 *outVar = u"callResult"_s;
1342 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1346 m_body += u'('
1348 + u')';
1349 }
1350 }
1351 m_body += u";\n";
1352
1355 }
1356
1357 for (int i = 0; i < argc; ++i) {
1358 const QQmlJSRegisterContent content = registerType(argv + i);
1359 const QString var = registerVariable(argv + i);
1360 args += u", "_s + contentPointer(content, var);
1361 types += u", "_s + contentType(content, var);
1362 }
1363
1364 return u"void *args[] = { "_s + args + u" };\n"_s
1365 + u"const QMetaType types[] = { "_s + types + u" };\n"_s;
1366}
1367
1368void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
1369{
1371 return;
1372
1374 m_body += u"std::move(" + outVar + u");\n";
1375}
1376
1378{
1379 Q_UNUSED(name)
1380 Q_UNUSED(argc)
1381 Q_UNUSED(argv)
1383}
1384
1385void QQmlJSCodeGenerator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
1386{
1387 Q_UNUSED(name)
1388 Q_UNUSED(thisObject)
1389 Q_UNUSED(argc)
1390 Q_UNUSED(argv)
1392}
1393
1394void QQmlJSCodeGenerator::generate_CallProperty(int nameIndex, int baseReg, int argc, int argv)
1395{
1396 Q_UNUSED(nameIndex);
1397 Q_UNUSED(baseReg);
1398 Q_UNUSED(argc);
1399 Q_UNUSED(argv);
1400 reject(u"CallProperty"_s);
1401}
1402
1403bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int argc, int argv)
1404{
1405 if (name != u"arg"_s || argc != 1)
1406 return false;
1407
1408 const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
1409 return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
1410 };
1411
1412 const auto ret = [&](const QString &arg) {
1413 const QString expression = convertStored(
1414 registerType(base).storedType(), m_typeResolver->stringType(),
1415 consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
1416 return conversion(
1418 };
1419
1422
1427 else
1429 m_body += u";\n"_s;
1430 return true;
1431}
1432
1433bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
1434{
1435 addInclude(u"qcoreapplication.h"_s);
1436
1437 const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
1438 Q_ASSERT(i < argc);
1439 return convertStored(registerType(argv + i).storedType(), type,
1440 consumedRegisterVariable(argv + i));
1441 };
1442
1443 const auto stringArg = [&](int i) {
1444 return i < argc
1445 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
1446 : u"\"\""_s;
1447 };
1448
1449 const auto intArg = [&](int i) {
1450 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
1451 };
1452
1453 const auto stringRet = [&](const QString &expression) {
1454 return conversion(
1456 };
1457
1458 const auto capture = [&]() {
1459 m_body += u"aotContext->captureTranslation();\n"_s;
1460 };
1461
1462 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
1463 Q_ASSERT(argc > 0);
1465 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
1466 return true;
1467 }
1468
1469 if (name == u"QT_TRANSLATE_NOOP"_s) {
1470 Q_ASSERT(argc > 1);
1472 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
1473 return true;
1474 }
1475
1476 if (name == u"qsTrId"_s) {
1477 capture();
1478 // We inline qtTrId() here because in the !QT_CONFIG(translation) case it's unavailable.
1479 // QCoreApplication::translate() is always available in some primitive form.
1480 // Also, this saves a function call.
1482 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
1483 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
1484 return true;
1485 }
1486
1487 if (name == u"qsTr"_s) {
1488 capture();
1490 + stringRet(u"QCoreApplication::translate("_s
1491 + u"aotContext->translationContext().toUtf8().constData(), "_s
1492 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1493 + intArg(2) + u")"_s) + u";\n"_s;
1494 return true;
1495 }
1496
1497 if (name == u"qsTranslate"_s) {
1498 capture();
1500 + stringRet(u"QCoreApplication::translate("_s
1501 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1502 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
1503 return true;
1504 }
1505
1506 return false;
1507}
1508
1509static QString maxExpression(int argc)
1510{
1511 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
1512
1513 QString expression =
1514 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1515 for (int i = 2; i < argc; i++) {
1516 expression +=
1517 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
1518 .arg("arg"_L1 + QString::number(i + 1));
1519 }
1520 expression += "return tmpMax;\n}()"_L1;
1521
1522 return expression;
1523}
1524
1525static QString minExpression(int argc)
1526{
1527 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
1528
1529 QString expression =
1530 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1531 for (int i = 2; i < argc; i++) {
1532 expression +=
1533 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
1534 .arg("arg"_L1 + QString::number(i + 1));
1535 }
1536 expression += "return tmpMin;\n}()"_L1;
1537
1538 return expression;
1539}
1540
1541bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
1542{
1543 addInclude(u"cmath"_s);
1544 addInclude(u"limits"_s);
1545 addInclude(u"qalgorithms.h"_s);
1546 addInclude(u"qrandom.h"_s);
1547 addInclude(u"qjsprimitivevalue.h"_s);
1548
1549 // If the result is not stored, we don't need to generate any code. All the math methods are
1550 // conceptually pure functions.
1552 return true;
1553
1554 m_body += u"{\n"_s;
1555 for (int i = 0; i < argc; ++i) {
1556 m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
1557 registerType(argv + i).storedType(),
1559 + u";\n"_s;
1560 }
1561
1562 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
1563 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
1565
1566 QString expression;
1567
1568 if (name == u"abs" && argc == 1) {
1569 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
1570 } else if (name == u"acos"_s && argc == 1) {
1571 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
1572 } else if (name == u"acosh"_s && argc == 1) {
1573 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
1574 } else if (name == u"asin"_s && argc == 1) {
1575 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
1576 } else if (name == u"asinh"_s && argc == 1) {
1577 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
1578 } else if (name == u"atan"_s && argc == 1) {
1579 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
1580 } else if (name == u"atanh"_s && argc == 1) {
1581 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
1582 } else if (name == u"atan2"_s) {
1583 // TODO: complicated
1584 return false;
1585 } else if (name == u"cbrt"_s && argc == 1) {
1586 expression = u"std::cbrt(arg1)"_s;
1587 } else if (name == u"ceil"_s && argc == 1) {
1588 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
1589 } else if (name == u"clz32"_s && argc == 1) {
1590 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
1591 } else if (name == u"cos"_s && argc == 1) {
1592 expression = u"std::cos(arg1)"_s;
1593 } else if (name == u"cosh"_s && argc == 1) {
1594 expression = u"std::cosh(arg1)"_s;
1595 } else if (name == u"exp"_s && argc == 1) {
1596 expression = u"std::isinf(arg1) "
1597 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
1598 ": std::exp(arg1)"_s.arg(inf);
1599 } else if (name == u"expm1"_s) {
1600 // TODO: complicated
1601 return false;
1602 } else if (name == u"floor"_s && argc == 1) {
1603 expression = u"std::floor(arg1)"_s;
1604 } else if (name == u"fround"_s && argc == 1) {
1605 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
1606 "? arg1 "
1607 ": double(float(arg1))"_s;
1608 } else if (name == u"hypot"_s) {
1609 // TODO: complicated
1610 return false;
1611 } else if (name == u"imul"_s && argc == 2) {
1612 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
1613 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
1614 } else if (name == u"log"_s && argc == 1) {
1615 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
1616 } else if (name == u"log10"_s && argc == 1) {
1617 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
1618 } else if (name == u"log1p"_s && argc == 1) {
1619 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
1620 } else if (name == u"log2"_s && argc == 1) {
1621 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
1622 } else if (name == u"max"_s && argc >= 2) {
1623 expression = maxExpression(argc);
1624 } else if (name == u"min"_s && argc >= 2) {
1625 expression = minExpression(argc);
1626 } else if (name == u"pow"_s) {
1627 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
1628 } else if (name == u"random"_s && argc == 0) {
1629 expression = u"QRandomGenerator::global()->generateDouble()"_s;
1630 } else if (name == u"round"_s && argc == 1) {
1631 expression = u"std::isfinite(arg1) "
1632 "? ((arg1 < 0.5 && arg1 >= -0.5) "
1633 "? std::copysign(0.0, arg1) "
1634 ": std::floor(arg1 + 0.5)) "
1635 ": arg1"_s;
1636 } else if (name == u"sign"_s && argc == 1) {
1637 expression = u"std::isnan(arg1) "
1638 "? %1 "
1639 ": (qIsNull(arg1) "
1640 "? arg1 "
1641 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
1642 } else if (name == u"sin"_s && argc == 1) {
1643 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
1644 } else if (name == u"sinh"_s && argc == 1) {
1645 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
1646 } else if (name == u"sqrt"_s && argc == 1) {
1647 expression = u"std::sqrt(arg1)"_s;
1648 } else if (name == u"tan"_s && argc == 1) {
1649 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
1650 } else if (name == u"tanh"_s && argc == 1) {
1651 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
1652 } else if (name == u"trunc"_s && argc == 1) {
1653 expression = u"std::trunc(arg1)"_s;
1654 } else {
1655 return false;
1656 }
1657
1659
1660 m_body += u";\n"_s;
1661 m_body += u"}\n"_s;
1662 return true;
1663}
1664
1666{
1667 if (method == u"log" || method == u"debug")
1668 return u"QtDebugMsg"_s;
1669 if (method == u"info")
1670 return u"QtInfoMsg"_s;
1671 if (method == u"warn")
1672 return u"QtWarningMsg"_s;
1673 if (method == u"error")
1674 return u"QtCriticalMsg"_s;
1675 return QString();
1676}
1677
1678bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int argv)
1679{
1681 if (type.isEmpty())
1682 return false;
1683
1684 addInclude(u"qloggingcategory.h"_s);
1685
1686 m_body += u"{\n";
1687 m_body += u" bool firstArgIsCategory = false;\n";
1688 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
1689
1690 // We could check for internalName == "QQmlLoggingCategory" here, but we don't want to
1691 // because QQmlLoggingCategory is not a builtin. Tying the specific internal name and
1692 // intheritance hierarchy in here would be fragile.
1693 // TODO: We could drop the check for firstArg in some cases if we made some base class
1694 // of QQmlLoggingCategory a builtin.
1695 const bool firstArgIsReference = argc > 0
1697
1698 if (firstArgIsReference) {
1699 m_body += u" QObject *firstArg = ";
1701 firstArg.storedType(),
1703 registerVariable(argv));
1704 m_body += u";\n";
1705 }
1706
1707 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
1708 m_body += firstArgIsReference ? u"firstArg" : u"nullptr";
1709 m_body += u", &firstArgIsCategory);\n";
1710 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
1711
1712 m_body += u" const QString message = ";
1713 if (argc > 0) {
1714 const QString firstArgStringConversion = convertStored(
1715 registerType(argv).storedType(),
1717 if (firstArgIsReference) {
1718 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
1719 if (argc > 1)
1720 m_body += u".append(QLatin1Char(' ')))).append(";
1721 else
1722 m_body += u"))";
1723 } else {
1724 m_body += firstArgStringConversion;
1725 if (argc > 1)
1726 m_body += u".append(QLatin1Char(' ')).append(";
1727 }
1728
1729 for (int i = 1; i < argc; ++i) {
1730 if (i > 1)
1731 m_body += u".append(QLatin1Char(' ')).append("_s;
1733 registerType(argv + i).storedType(),
1735 }
1736 } else {
1737 m_body += u"QString()";
1738 }
1739 m_body += u";\n";
1740
1741 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
1742 m_body += u" }\n";
1743 m_body += u"}\n";
1744 return true;
1745}
1746
1747bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
1748{
1749 const auto intType = m_typeResolver->int32Type();
1750 const auto valueType = registerType(base).storedType()->valueType();
1751 const auto boolType = m_typeResolver->boolType();
1752 const auto stringType = m_typeResolver->stringType();
1753 const auto baseType = registerType(base);
1754
1755 const QString baseVar = registerVariable(base);
1756 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
1757 + name + u"(";
1758
1759 addInclude(u"qjslist.h"_s);
1760
1761 if (name == u"includes" && argc > 0 && argc < 3) {
1762 QString call = qjsListMethod
1763 + convertStored(registerType(argv).storedType(), valueType,
1765 if (argc == 2) {
1766 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
1767 consumedRegisterVariable(argv + 1));
1768 }
1769 call += u")";
1770
1772 + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
1773 return true;
1774 }
1775
1776 if (name == u"toString" || (name == u"join" && argc < 2)) {
1777 QString call = qjsListMethod;
1778 if (argc == 1) {
1779 call += convertStored(registerType(argv).storedType(), stringType,
1781 }
1782 call += u")";
1783
1785 + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
1786 return true;
1787 }
1788
1789 if (name == u"slice" && argc < 3) {
1790 QString call = qjsListMethod;
1791 for (int i = 0; i < argc; ++i) {
1792 if (i > 0)
1793 call += u", ";
1794 call += convertStored(registerType(argv + i).storedType(), intType,
1795 consumedRegisterVariable(argv + i));
1796 }
1797 call += u")";
1798
1799 const auto outType = baseType.storedType()->isListProperty()
1801 : baseType;
1802
1804 + conversion(outType, m_state.accumulatorOut(), call) + u";\n"_s;
1805 return true;
1806 }
1807
1808 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
1809 QString call = qjsListMethod
1810 + convertStored(registerType(argv).storedType(), valueType,
1812 if (argc == 2) {
1813 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
1814 consumedRegisterVariable(argv + 1));
1815 }
1816 call += u")";
1817
1819 + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
1820 return true;
1821 }
1822
1823 return false;
1824}
1825
1827{
1829
1831 reject(u"call to untyped JavaScript function"_s);
1832
1833 AccumulatorConverter registers(this);
1834
1835 const QQmlJSRegisterContent baseType = registerType(base);
1836 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1839 if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) {
1840 if (inlineMathMethod(name, argc, argv))
1841 return;
1842 }
1843
1844 if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), consoleObject())) {
1845 if (inlineConsoleMethod(name, argc, argv))
1846 return;
1847 }
1848
1851 if (inlineStringMethod(name, base, argc, argv))
1852 return;
1853 }
1854
1855 if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
1856 if (inlineArrayMethod(name, base, argc, argv))
1857 return;
1858 }
1859
1860 // This is possible, once we establish the right kind of lookup for it
1861 reject(u"call to property '%1' of %2"_s.arg(name, baseType.descriptiveName()));
1862 }
1863
1864 const QString indexString = QString::number(index);
1865
1866 m_body += u"{\n"_s;
1867
1868 QString outVar;
1869 m_body += argumentsList(argc, argv, &outVar);
1870 const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
1871 + u", "_s + registerVariable(base)
1872 + u", args, types, "_s + QString::number(argc) + u')';
1873 const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
1874 + indexString + u')';
1875 generateLookup(lookup, initialization);
1876 generateMoveOutVar(outVar);
1877
1878 m_body += u"}\n"_s;
1879}
1880
1882{
1883 Q_UNUSED(name);
1884 Q_UNUSED(argc);
1885 Q_UNUSED(argv);
1886 reject(u"CallName"_s);
1887}
1888
1890{
1891 Q_UNUSED(argc)
1892 Q_UNUSED(argv)
1894}
1895
1897{
1898 Q_UNUSED(index);
1899 Q_UNUSED(argc);
1900 Q_UNUSED(argv);
1901 reject(u"CallGlobalLookup"_s);
1902}
1903
1905{
1907
1909 reject(u"call to untyped JavaScript function"_s);
1910
1915 if (inlineTranslateMethod(name, argc, argv))
1916 return;
1917 }
1918
1919 AccumulatorConverter registers(this);
1920
1921 const QString indexString = QString::number(index);
1922
1923 m_body += u"{\n"_s;
1924 QString outVar;
1925 m_body += argumentsList(argc, argv, &outVar);
1926 const QString lookup = u"aotContext->callQmlContextPropertyLookup("_s + indexString
1927 + u", args, types, "_s + QString::number(argc) + u')';
1928 const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_s
1929 + indexString + u')';
1930 generateLookup(lookup, initialization);
1931 generateMoveOutVar(outVar);
1932
1933 m_body += u"}\n"_s;
1934}
1935
1936void QQmlJSCodeGenerator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
1937{
1938 Q_UNUSED(func)
1939 Q_UNUSED(thisObject)
1940 Q_UNUSED(argc)
1941 Q_UNUSED(argv)
1943}
1944
1945void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc, int argv)
1946{
1947 Q_UNUSED(func)
1948 Q_UNUSED(thisObject)
1949 Q_UNUSED(argc)
1950 Q_UNUSED(argv)
1952}
1953
1955{
1956 Q_UNUSED(func);
1957 Q_UNUSED(argc);
1958 Q_UNUSED(argv);
1959 reject(u"Construct"_s);
1960}
1961
1963{
1964 Q_UNUSED(func)
1965 Q_UNUSED(argc)
1966 Q_UNUSED(argv)
1968}
1969
1971{
1973 reject(u"SetUnwindHandlerh"_s);
1974}
1975
1977{
1978 reject(u"UnwindDispatch"_s);
1979}
1980
1982{
1986}
1987
1989{
1990 Q_UNUSED(name)
1991 // Nothing to do here. If we have statically asserted the dtz check in the type propagator
1992 // the value cannot be empty. Otherwise we can't get here.
1993}
1994
1996{
1998
2000 m_body += u"aotContext->engine->throwError("_s
2003 m_state.accumulatorVariableIn) + u");\n"_s;
2004 m_body += u"return "_s + errorReturnValue() + u";\n"_s;
2005 m_skipUntilNextLabel = true;
2006 resetState();
2007}
2008
2010{
2012}
2013
2015{
2017}
2018
2020{
2022
2023 m_body += u"{\n"_s;
2024}
2025
2027{
2029 Q_UNUSED(nameIndex)
2030 reject(u"PushCatchContext"_s);
2031}
2032
2034{
2036}
2037
2039{
2042}
2043
2045{
2047}
2048
2050{
2053}
2054
2056{
2058}
2059
2061{
2063
2064 // Add a semicolon before the closing brace, in case there was a bare label before it.
2065 m_body += u";}\n"_s;
2066}
2067
2069{
2070 Q_UNUSED(iterator)
2072}
2073
2075{
2077 Q_UNUSED(done)
2079}
2080
2082{
2083 Q_UNUSED(iterator)
2084 Q_UNUSED(object)
2086}
2087
2089{
2090 Q_UNUSED(done)
2092}
2093
2095{
2097}
2098
2100{
2101 Q_UNUSED(base)
2104}
2105
2107{
2108 Q_UNUSED(name)
2110}
2111
2113{
2114 Q_UNUSED(name);
2115 reject(u"TypeofName"_s);
2116}
2117
2119{
2120 reject(u"TypeofValue"_s);
2121}
2122
2123void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
2124{
2125 Q_UNUSED(varName)
2126 Q_UNUSED(isDeletable)
2128}
2129
2131{
2133
2135 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
2136 // This rejects any attempt to store the list into a QVariant.
2137 // Therefore, we don't have to adjust the contained type below.
2138 reject(u"storing an array in a non-sequence type"_s);
2139 return;
2140 }
2141
2142 const QQmlJSScope::ConstPtr value = stored->valueType();
2143 Q_ASSERT(value);
2144
2145 QStringList initializer;
2146 for (int i = 0; i < argc; ++i) {
2147 initializer += convertStored(
2148 registerType(args + i).storedType(), value,
2150 }
2151
2152 if (stored->isListProperty()) {
2153 reject(u"creating a QQmlListProperty not backed by a property"_s);
2154
2155 // We can, technically, generate code for this. But it's dangerous:
2156 //
2157 // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
2158 // m_body += stored->internalName() + u"::ListType " + storage
2159 // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
2160 // m_body += m_state.accumulatorVariableOut
2161 // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
2162 } else {
2163 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
2164 m_body += initializer.join(u", "_s);
2165 m_body += u"};\n";
2166 }
2167}
2168
2169void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
2170{
2172
2174 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value) {
2175 reject(u"storing an object literal in a non-value type"_s);
2176 return;
2177 }
2178
2180
2182 const bool isVariantOrPrimitive = m_typeResolver->equals(stored, m_typeResolver->varType())
2184
2186 m_body += u"()";
2187 } else if (isVariantOrPrimitive) {
2189 } else {
2190 reject(u"storing an object literal in an unsupported container %1"_s
2191 .arg(stored->internalName()));
2192 }
2193 m_body += u";\n";
2194
2195 bool isExtension = false;
2196 if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension)) {
2197 reject(u"storing an object literal in a non-structured value type"_s);
2198 }
2199
2200 const QQmlJSScope::ConstPtr accessor = isExtension
2201 ? contained->extensionType().scope
2202 : contained;
2203
2204 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2205 Q_ASSERT(argc >= classSize);
2206 if (argc == 0)
2207 return;
2208
2209 m_body += u"{\n";
2210 m_body += u" const QMetaObject *meta = ";
2211 if (!isExtension && isVariantOrPrimitive)
2212 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
2213 else
2214 m_body += metaObject(accessor);
2215 m_body += u";\n";
2216
2217 for (int i = 0; i < classSize; ++i) {
2218 m_body += u" {\n";
2219 const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
2220 const int currentArg = args + i;
2221 const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
2222 const QQmlJSRegisterContent argType = registerType(currentArg);
2223 const QQmlJSMetaProperty property = contained->property(propName);
2224 const QString consumedArg = consumedRegisterVariable(currentArg);
2225 QString argument = conversion(argType, propType, consumedArg);
2226
2227 if (argument == consumedArg) {
2228 argument = registerVariable(currentArg);
2229 } else {
2230 m_body += u" auto arg = "_s + argument + u";\n";
2231 argument = u"arg"_s;
2232 }
2233
2234 int index = property.index();
2235 if (index == -1)
2236 continue;
2237
2238 m_body += u" void *argv[] = { %1, nullptr };\n"_s
2239 .arg(contentPointer(propType, argument));
2240 m_body += u" meta->d.static_metacall(reinterpret_cast<QObject *>(";
2242 m_body += u"), QMetaObject::WriteProperty, ";
2243 m_body += QString::number(index) + u", argv);\n";
2244 m_body += u" }\n";
2245 }
2246
2247 // This is not implemented because we cannot statically determine the type of the value and we
2248 // don't want to rely on QVariant::convert() since that may give different results than
2249 // the JavaScript coercion. We might still make it work by querying the QMetaProperty
2250 // for its type at run time and runtime coercing to that, but we don't know whether that
2251 // still pays off.
2252 if (argc > classSize)
2253 reject(u"non-literal keys of object literals"_s);
2254
2255 m_body += u"}\n";
2256
2257}
2258
2259void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
2260{
2261 Q_UNUSED(classIndex)
2262 Q_UNUSED(heritage)
2263 Q_UNUSED(computedNames)
2265}
2266
2268{
2270}
2271
2273{
2275}
2276
2278{
2279 Q_UNUSED(argIndex)
2281}
2282
2284{
2286
2287 m_body += changedRegisterVariable() + u" = "_s
2289 u"aotContext->thisObject()"_s)
2290 + u";\n"_s;
2291}
2292
2294{
2296}
2297
2299{
2301}
2302
2304{
2306
2307 generateJumpCodeWithTypeConversions(offset);
2308 m_body += u";\n"_s;
2309 m_skipUntilNextLabel = true;
2310 resetState();
2311}
2312
2314{
2316
2317 m_body += u"if ("_s;
2320 m_body += u") "_s;
2321 generateJumpCodeWithTypeConversions(offset);
2322 m_body += u";\n"_s;
2323}
2324
2326{
2328
2329 m_body += u"if (!"_s;
2332 m_body += u") "_s;
2333 generateJumpCodeWithTypeConversions(offset);
2334 m_body += u";\n"_s;
2335}
2336
2338{
2340
2341 m_body += u"if (!context->engine->hasException()) "_s;
2342 generateJumpCodeWithTypeConversions(offset);
2343 m_body += u";\n"_s;
2344}
2345
2347{
2350}
2351
2353{
2355
2356 generateExceptionCheck();
2357}
2358
2360{
2361 INJECT_TRACE_INFO(generate_CmlEqNull);
2362
2364 m_body += conversion(
2366 u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_s
2367 + convertStored(
2370 m_body += u";\n"_s;
2371}
2372
2374{
2375 INJECT_TRACE_INFO(generate_CmlNeNull);
2376
2378 m_body += conversion(
2380 u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_s
2381 + convertStored(
2384 m_body += u";\n"_s;
2385}
2386
2387QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
2388{
2390 return QString::number(lhsConst) + u" == "_s + m_state.accumulatorVariableIn;
2391
2393 return QString::number(lhsConst) + u" == "_s
2396 }
2397
2400 QString::number(lhsConst)) + u" == "_s
2403 }
2404
2407 QString::number(lhsConst));
2408 result += u".equals("_s;
2411 result += u')';
2412 return result;
2413}
2414
2416 const QQmlJSRegisterContent &content, const QString &var, int lookup)
2417{
2418 if (m_typeResolver->registerContains(content, content.storedType()))
2419 return QString();
2420
2422 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
2423 + QString::number(lookup) + u"))"_s;
2424 }
2425 // TODO: We could make sure they're compatible, for example QObject pointers.
2426 return QString();
2427}
2428
2430{
2431 const QQmlJSScope::ConstPtr stored = content.storedType();
2432 if (m_typeResolver->registerContains(content, stored))
2433 return u'&' + var;
2434
2437 return var + u".data()"_s;
2438 }
2439
2440 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2441 return u'&' + var;
2442
2443 if (m_typeResolver->isNumeric(content.storedType())
2445 return u'&' + var;
2446 }
2447
2448 if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
2449 return u'&' + var;
2450
2451 reject(u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
2452 return QString();
2453}
2454
2456{
2457 const QQmlJSScope::ConstPtr stored = content.storedType();
2459 m_typeResolver->containedType(content));
2460 if (m_typeResolver->equals(contained, stored))
2461 return metaTypeFromType(stored);
2462
2465 return var + u".metaType()"_s; // We expect the container to be initialized
2466 }
2467
2468 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2469 return metaTypeFromName(contained);
2470
2471 if (m_typeResolver->isNumeric(stored) && contained->scopeType() == QQmlSA::ScopeType::EnumScope)
2472 return metaTypeFromType(contained->baseType());
2473
2474 if (stored->isListProperty() && m_typeResolver->containedType(content)->isListProperty())
2476
2477 reject(u"content type of unsupported wrapper type "_s + content.descriptiveName());
2478 return QString();
2479}
2480
2482{
2484
2487 eqIntExpression(lhsConst)) + u";\n"_s;
2488}
2489
2491{
2493
2496 u"!("_s + eqIntExpression(lhsConst) + u')') + u";\n"_s;
2497}
2498
2500{
2502 generateEqualityOperation(lhs, u"equals"_s, false);
2503}
2504
2506{
2508 generateEqualityOperation(lhs, u"equals"_s, true);
2509}
2510
2512{
2514 generateCompareOperation(lhs, u">"_s);
2515}
2516
2518{
2520 generateCompareOperation(lhs, u">="_s);
2521}
2522
2524{
2526 generateCompareOperation(lhs, u"<"_s);
2527}
2528
2530{
2532 generateCompareOperation(lhs, u"<="_s);
2533}
2534
2536{
2538 generateEqualityOperation(lhs, u"strictlyEquals"_s, false);
2539}
2540
2542{
2544 generateEqualityOperation(lhs, u"strictlyEquals"_s, true);
2545}
2546
2548{
2549 Q_UNUSED(lhs)
2550 reject(u"CmpIn"_s);
2551}
2552
2554{
2555 Q_UNUSED(lhs)
2557}
2558
2560{
2562
2563 const QString input = registerVariable(lhs);
2564 const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
2565 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
2566
2567 // If the original output is a conversion, we're supposed to check for the contained
2568 // type and if it doesn't match, set the result to null or undefined.
2569 const QQmlJSRegisterContent originalContent = m_typeResolver->original(outputContent);
2570
2571 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(originalContent);
2572
2573 if (contained->isReferenceType()) {
2574 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(contained);
2575 const QString inputConversion = inputContent.storedType()->isReferenceType()
2576 ? input
2577 : convertStored(inputContent.storedType(), genericContained, input);
2578
2582 && contained->isComposite()) {
2583 m_body += conversion(
2584 genericContained, outputContent,
2585 m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
2586 } else {
2587 m_body += conversion(
2588 genericContained, outputContent,
2589 u'(' + metaObject(contained) + u")->cast("_s + inputConversion + u')');
2590 }
2591 m_body += u";\n"_s;
2592 return;
2593 } else if (m_typeResolver->equals(inputContent.storedType(), m_typeResolver->varType())) {
2594 if (originalContent.isConversion()) {
2595 const auto origins = originalContent.conversionOrigins();
2596 Q_ASSERT(origins.size() == 2);
2597
2599 ? origins[1]
2600 : origins[0];
2601
2603
2605 m_body += input + u".metaType() == "_s + metaType(target)
2606 + u" ? " + conversion(inputContent, outputContent, input)
2608 outputContent, QString());
2609 m_body += u";\n"_s;
2610 return;
2611 }
2612 }
2613
2614 reject(u"unsupported type assertion"_s);
2615
2616}
2617
2619{
2621 generateUnaryOperation(u"!"_s);
2622}
2623
2625{
2627 generateUnaryOperation(u"+"_s);
2628}
2629
2631{
2633 generateUnaryOperation(u"-"_s);
2634}
2635
2637{
2639 generateUnaryOperation(u"~"_s);
2640}
2641
2643{
2645 generateInPlaceOperation(u"++"_s);
2646}
2647
2649{
2651 generateInPlaceOperation(u"--"_s);
2652}
2653
2655{
2657 generateArithmeticOperation(lhs, u"+"_s);
2658}
2659
2661{
2663 generateArithmeticOperation(lhs, u"&"_s);
2664}
2665
2667{
2669 generateArithmeticOperation(lhs, u"|"_s);
2670}
2671
2673{
2675 generateArithmeticOperation(lhs, u"^"_s);
2676}
2677
2679{
2680 INJECT_TRACE_INFO(generate_BitUShr);
2681 generateShiftOperation(lhs, u">>"_s);
2682}
2683
2685{
2687 generateShiftOperation(lhs, u">>"_s);
2688}
2689
2691{
2693 generateShiftOperation(lhs, u"<<"_s);
2694}
2695
2697{
2699 generateArithmeticConstOperation(rhs, u"&"_s);
2700}
2701
2703{
2705 generateArithmeticConstOperation(rhs, u"|"_s);
2706}
2707
2709{
2711 generateArithmeticConstOperation(rhs, u"^"_s);
2712}
2713
2715{
2717 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
2718}
2719
2721{
2723 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
2724}
2725
2727{
2729 generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
2730}
2731
2733{
2735
2736 const QString lhsString = conversion(
2738 const QString rhsString = conversion(
2741
2742 Q_ASSERT(m_error->isValid() || !lhsString.isEmpty());
2743 Q_ASSERT(m_error->isValid() || !rhsString.isEmpty());
2744
2747 m_body += conversion(
2748 originalOut, m_state.accumulatorOut(),
2749 u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
2750 m_body += u";\n"_s;
2751}
2752
2754{
2756 generateArithmeticOperation(lhs, u"*"_s);
2757}
2758
2760{
2762 generateArithmeticOperation(lhs, u"/"_s);
2763}
2764
2766{
2768
2769 const auto lhsVar = convertStored(
2770 registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
2772 const auto rhsVar = convertStored(
2775 Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
2776 Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
2777
2779 m_body += u" = "_s;
2781 u'(' + lhsVar + u" % "_s + rhsVar + u')');
2782 m_body += u";\n"_s;
2783}
2784
2786{
2788 generateArithmeticOperation(lhs, u"-"_s);
2789}
2790
2792{
2793 Q_UNUSED(firstReg)
2795 // Ignore. We reject uninitialized values anyway.
2796}
2797
2799{
2801}
2802
2804{
2807}
2808
2811{
2812 m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
2813 const auto accumulatorIn = m_state.registers.find(Accumulator);
2814 if (accumulatorIn != m_state.registers.end()
2815 && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
2816 m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
2817 .value(accumulatorIn.value().content.storedType());
2819 } else {
2821 }
2822
2823 auto labelIt = m_labels.constFind(currentInstructionOffset());
2824 if (labelIt != m_labels.constEnd()) {
2825 m_body += *labelIt + u":;\n"_s;
2826 m_skipUntilNextLabel = false;
2827 } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
2828 return SkipInstruction;
2829 }
2830
2833 else
2835
2836 // If the accumulator type is valid, we want an accumulator variable.
2837 // If not, we don't want one.
2843
2844 // If the instruction has no side effects and doesn't write any register, it's dead.
2845 // We might still need the label, though, and the source code comment.
2846 if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty())
2847 return SkipInstruction;
2848
2849 return ProcessInstruction;
2850}
2851
2853{
2854 if (!m_skipUntilNextLabel)
2855 generateJumpCodeWithTypeConversions(0);
2856}
2857
2859{
2860 m_body += u"aotContext->setInstructionPointer("_s
2861 + QString::number(nextInstructionOffset()) + u");\n"_s;
2862}
2863
2864void QQmlJSCodeGenerator::generateExceptionCheck()
2865{
2866 m_body += u"if (aotContext->engine->hasError())\n"_s;
2867 m_body += u" return "_s + errorReturnValue() + u";\n"_s;
2868}
2869
2870void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &function, bool invert)
2871{
2872 const QQmlJSRegisterContent lhsContent = registerType(lhs);
2873 const bool strictlyComparableWithVar = function == "strictlyEquals"_L1
2875 auto isComparable = [&]() {
2876 if (m_typeResolver->isPrimitive(lhsContent)
2878 return true;
2879 }
2881 return true;
2883 return true;
2884 if (strictlyComparableWithVar)
2885 return true;
2887 return true;
2889 return true;
2890 return false;
2891 };
2892
2893 if (!isComparable()) {
2894 reject(u"incomparable types %1 and %2"_s.arg(m_state.accumulatorIn().descriptiveName(),
2895 lhsContent.descriptiveName()));
2896 }
2897
2898 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
2900
2901 const auto primitive = m_typeResolver->jsPrimitiveType();
2902 if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
2904 if (isTypeStorable(m_typeResolver, lhsType)) {
2906 consumedRegisterVariable(lhs) + (invert ? u" != "_s : u" == "_s)
2908 } else {
2909 // null === null and undefined === undefined
2910 m_body += invert ? u"false"_s : u"true"_s;
2911 }
2912 } else if (strictlyComparableWithVar) {
2913 // Determine which side is holding a storable type
2914 if (const auto registerVariableName = registerVariable(lhs);
2915 !registerVariableName.isEmpty()) {
2916 // lhs register holds var type
2917 generateVariantEqualityComparison(m_state.accumulatorIn(), registerVariableName,
2918 invert);
2919 } else {
2920 // lhs content is not storable and rhs is var type
2921 generateVariantEqualityComparison(lhsContent, m_state.accumulatorVariableIn, invert);
2922 }
2923 } else if (canCompareWithQObject(m_typeResolver, lhsContent, m_state.accumulatorIn())) {
2925 m_body += u'('
2926 + (isTypeStorable(m_typeResolver, lhsContent.storedType())
2927 ? registerVariable(lhs)
2928 : m_state.accumulatorVariableIn)
2929 + (invert ? u" != "_s : u" == "_s) + u"nullptr)"_s;
2930 } else {
2932 m_body += conversion(
2934 (invert ? u"!"_s : QString())
2935 + convertStored(registerType(lhs).storedType(), primitive,
2937 + u'.' + function + u'(' + convertStored(
2938 m_state.accumulatorIn().storedType(), primitive,
2940 + u')');
2941 }
2942 m_body += u";\n"_s;
2943}
2944
2945void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
2946{
2948
2949 const auto lhsType = registerType(lhs);
2950 const QQmlJSScope::ConstPtr compareType =
2954
2955 m_body += conversion(
2957 convertStored(registerType(lhs).storedType(), compareType,
2959 + u' ' + cppOperator + u' '
2960 + convertStored(m_state.accumulatorIn().storedType(), compareType,
2962 m_body += u";\n"_s;
2963}
2964
2965void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
2966{
2967 generateArithmeticOperation(
2972 cppOperator);
2973}
2974
2975void QQmlJSCodeGenerator::generateShiftOperation(int lhs, const QString &cppOperator)
2976{
2977 generateArithmeticOperation(
2981 consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
2982 cppOperator);
2983}
2984
2985void QQmlJSCodeGenerator::generateArithmeticOperation(
2986 const QString &lhs, const QString &rhs, const QString &cppOperator)
2987{
2988 Q_ASSERT(m_error->isValid() || !lhs.isEmpty());
2989 Q_ASSERT(m_error->isValid() || !rhs.isEmpty());
2990
2993 m_body += u" = "_s;
2994 m_body += conversion(
2995 originalOut, m_state.accumulatorOut(),
2996 u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
2997 m_body += u";\n"_s;
2998}
2999
3000void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
3001{
3002 generateArithmeticOperation(
3007 cppOperator);
3008}
3009
3010void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
3011{
3014
3016 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3017 return;
3018 }
3019
3020 const auto original = m_typeResolver->original(m_state.accumulatorOut());
3021 if (m_state.accumulatorOut() == original) {
3022 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3024 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3025 return;
3026 }
3027
3029 original, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
3030}
3031
3032void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
3033{
3034 {
3035 // If actually in place, we cannot consume the variable.
3039 m_body += cppOperator + var + u";\n"_s;
3040 return;
3041 }
3042 }
3043
3046
3047 const auto original = m_typeResolver->original(m_state.accumulatorOut());
3048 if (m_state.accumulatorOut() == original) {
3049 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3050 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3051 return;
3052 }
3053
3054 m_body += u"{\n"_s;
3055 m_body += u"auto converted = "_s + var + u";\n"_s;
3057 original, m_state.accumulatorOut(), u'('
3058 + cppOperator + u"converted)"_s) + u";\n"_s;
3059 m_body += u"}\n"_s;
3060}
3061
3062void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
3063 const QString &resultPreparation)
3064{
3065 if (!resultPreparation.isEmpty())
3066 m_body += resultPreparation + u";\n"_s;
3067 m_body += u"while (!"_s + lookup + u") {\n"_s;
3069 m_body += initialization + u";\n"_s;
3070 generateExceptionCheck();
3071 if (!resultPreparation.isEmpty())
3072 m_body += resultPreparation + u";\n"_s;
3073 m_body += u"}\n"_s;
3074}
3075
3076void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset)
3077{
3078 QString conversionCode;
3079 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
3080 const auto annotation = m_annotations->find(absoluteOffset);
3081 if (annotation != m_annotations->constEnd()) {
3082 const auto &conversions = annotation->second.typeConversions;
3083
3084 for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
3085 regIt != regEnd; ++regIt) {
3086 int registerIndex = regIt.key();
3087 const QQmlJSRegisterContent targetType = regIt.value().content;
3088 if (!targetType.isValid())
3089 continue;
3090
3091 QQmlJSRegisterContent currentType;
3092 QString currentVariable;
3093 if (registerIndex == m_state.changedRegisterIndex()) {
3094 currentType = m_state.changedRegister();
3095 currentVariable = changedRegisterVariable();
3096 } else {
3097 auto it = m_state.registers.find(registerIndex);
3098 if (it == m_state.registers.end())
3099 continue;
3100 currentType = it.value().content;
3101 currentVariable = consumedRegisterVariable(registerIndex);
3102 }
3103
3104 // Actually == here. We want the jump code also for equal types
3105 if (currentType == targetType
3106 || !isTypeStorable(m_typeResolver, targetType.storedType())) {
3107 continue;
3108 }
3109
3110 Q_ASSERT(m_registerVariables.contains(registerIndex));
3111 const auto &currentRegisterVariables = m_registerVariables[registerIndex];
3112 const auto variable = currentRegisterVariables.constFind(targetType.storedType());
3113 if (variable == currentRegisterVariables.end() || *variable == currentVariable)
3114 continue;
3115
3116 conversionCode += *variable;
3117 conversionCode += u" = "_s;
3118 conversionCode += conversion(currentType, targetType, currentVariable);
3119 conversionCode += u";\n"_s;
3120 }
3121 }
3122
3123 if (relativeOffset) {
3124 auto labelIt = m_labels.find(absoluteOffset);
3125 if (labelIt == m_labels.end())
3126 labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
3127 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
3128 }
3129
3130 if (!conversionCode.isEmpty())
3131 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
3132}
3133
3135{
3136 auto it = m_registerVariables.find(index);
3137 if (it != m_registerVariables.end()) {
3138 const QString variable = it->value(registerType(index).storedType());
3139 if (!variable.isEmpty())
3140 return variable;
3141 }
3142
3143 return QString();
3144}
3145
3147{
3149 if (var.isEmpty() || !shouldMoveRegister(index))
3150 return var;
3151 return u"std::move(" + var + u")";
3152}
3153
3155{
3157 ? u"std::move(" + m_state.accumulatorVariableIn + u")"
3159}
3160
3162{
3163 return m_registerVariables.value(m_state.changedRegisterIndex()).value(
3165}
3166
3168{
3169 auto it = m_state.registers.find(index);
3170 if (it != m_state.registers.end())
3171 return it.value().content;
3172
3173 return QQmlJSRegisterContent();
3174}
3175
3177{
3180}
3181
3183 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
3184{
3185 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(to);
3186
3187 // If both types are stored in QJSPrimitiveValue we coerce using QJSPrimitiveValue
3191 return variable;
3192
3193 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
3194 if (m_typeResolver->equals(contained, m_typeResolver->boolType()))
3195 return conversion.arg(u"Boolean"_s);
3196 if (m_typeResolver->isIntegral(to))
3197 return conversion.arg(u"Integer"_s);
3198 if (m_typeResolver->equals(contained, m_typeResolver->realType()))
3199 return conversion.arg(u"Double"_s);
3200 if (m_typeResolver->equals(contained, m_typeResolver->stringType()))
3201 return conversion.arg(u"String"_s);
3202 reject(u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
3203 }
3204
3205 if (m_typeResolver->registerIsStoredIn(to, contained)
3207 || to.storedType()->isReferenceType()
3208 || m_typeResolver->registerContains(from, contained)) {
3209 // If:
3210 // * the output is not actually wrapped at all, or
3211 // * the output is stored in a numeric type (as there are no internals to a number), or
3212 // * the output is a QObject pointer, or
3213 // * we merely wrap the value into a new container,
3214 // we can convert by stored type.
3215 return convertStored(from.storedType(), to.storedType(), variable);
3216 } else {
3217 return convertContained(from, to, variable);
3218 }
3219}
3220
3222 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
3223{
3224 // TODO: most values can be moved, which is much more efficient with the common types.
3225 // add a move(from, to, variable) function that implements the moves.
3226 Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
3227
3228 const auto jsValueType = m_typeResolver->jsValueType();
3229 const auto varType = m_typeResolver->varType();
3230 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
3231 const auto boolType = m_typeResolver->boolType();
3232
3233 auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
3234 if (m_typeResolver->equals(to, boolType))
3235 return u"false"_s;
3237 return u"0"_s;
3239 return u"0u"_s;
3240 return QString();
3241 };
3242
3244 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3245 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3246 const QString zero = zeroBoolOrInt(to);
3247 if (!zero.isEmpty())
3248 return zero;
3250 return u"std::numeric_limits<float>::quiet_NaN()"_s;
3252 return u"std::numeric_limits<double>::quiet_NaN()"_s;
3254 return QQmlJSUtils::toLiteral(u"undefined"_s);
3255 if (m_typeResolver->equals(from, to))
3256 return QString();
3257 // Anything else is just the default constructed type.
3258 return to->augmentedInternalName() + u"()"_s;
3259 }
3260
3262 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3263 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3264 if (m_typeResolver->equals(to, jsValueType))
3265 return u"QJSValue(QJSValue::NullValue)"_s;
3266 if (m_typeResolver->equals(to, jsPrimitiveType))
3267 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
3268 if (m_typeResolver->equals(to, varType))
3269 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
3270 const QString zero = zeroBoolOrInt(to);
3271 if (!zero.isEmpty())
3272 return zero;
3274 return u"0.0f"_s;
3276 return u"0.0"_s;
3278 return QQmlJSUtils::toLiteral(u"null"_s);
3279 if (m_typeResolver->equals(from, to))
3280 return QString();
3281 reject(u"Conversion from null to %1"_s.arg(to->internalName()));
3282 }
3283
3284 if (m_typeResolver->equals(from, to))
3285 return variable;
3286
3287 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
3288 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
3289 // Compare internalName here. The same C++ type can be exposed muliple times in
3290 // different QML types. However, the C++ names have to be unique. We can always
3291 // static_cast to those.
3292
3293 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
3294 // We still have to cast as other execution paths may result in different types.
3295 if (base->internalName() == to->internalName())
3296 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
3297 }
3298 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
3299 if (base->internalName() == from->internalName())
3300 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
3301 }
3302 } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
3303 return u'(' + variable + u" != nullptr)"_s;
3304 }
3305 }
3306
3307 auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
3308 return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
3309 };
3310
3311 if (isJsValue(from) && isJsValue(to))
3312 return variable;
3313
3314 const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
3317 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
3318 };
3319
3323 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
3325 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
3327 return u'(' + variable + u" && !std::isnan("_s + variable + u"))"_s;
3328 }
3329
3330 if (isBoolOrNumber(from) && isBoolOrNumber(to))
3331 return to->internalName() + u'(' + variable + u')';
3332
3333
3334 if (m_typeResolver->equals(from, jsPrimitiveType)) {
3336 return variable + u".toDouble()"_s;
3337 if (m_typeResolver->equals(to, boolType))
3338 return variable + u".toBoolean()"_s;
3340 return variable + u".toInteger()"_s;
3342 return u"uint("_s + variable + u".toInteger())"_s;
3344 return variable + u".toString()"_s;
3345 if (m_typeResolver->equals(to, jsValueType))
3346 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
3347 if (m_typeResolver->equals(to, varType))
3348 return variable + u".toVariant()"_s;
3349 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3350 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3351 }
3352
3353 if (isJsValue(from)) {
3354 if (m_typeResolver->equals(to, jsPrimitiveType))
3355 return variable + u".toPrimitive()"_s;
3356 if (m_typeResolver->equals(to, varType))
3357 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
3358 return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
3359 }
3360
3361 if (m_typeResolver->equals(to, jsPrimitiveType)) {
3362 // null and undefined have been handled above already
3365
3370 return u"QJSPrimitiveValue("_s + variable + u')';
3371 } else if (m_typeResolver->isSignedInteger(from)
3374 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
3375 } else if (m_typeResolver->isNumeric(from)) {
3376 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
3377 }
3378 }
3379
3380 if (m_typeResolver->equals(to, jsValueType))
3381 return u"aotContext->engine->toScriptValue("_s + variable + u')';
3382
3383 if (m_typeResolver->equals(from, varType)) {
3385 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
3386 return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
3387 + variable + u')';
3388 }
3389
3390 if (m_typeResolver->equals(to, varType))
3391 return u"QVariant::fromValue("_s + variable + u')';
3392
3395 return variable + u".toString()"_s;
3396 }
3397
3400 return u"QUrl("_s + variable + u')';
3401 }
3402
3405 return u"QString::fromUtf8("_s + variable + u')';
3406 }
3407
3410 return variable + u".toUtf8()"_s;
3411 }
3412
3413 for (const auto &originType : {
3417 if (m_typeResolver->equals(from, originType)) {
3418 for (const auto &targetType : {
3423 if (m_typeResolver->equals(to, targetType)) {
3424 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
3425 originType->internalName(), targetType->internalName(), variable);
3426 }
3427 }
3428 break;
3429 }
3430 }
3431
3432 const auto retrieveFromPrimitive = [&](
3433 const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
3434 {
3436 return expression + u".toBoolean()"_s;
3438 return expression + u".toInteger()"_s;
3440 return u"uint("_s + expression + u".toInteger())"_s;
3442 return expression + u".toDouble()"_s;
3444 return u"float("_s + expression + u".toDouble())"_s;
3446 return expression + u".toString()"_s;
3447 return QString();
3448 };
3449
3450 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
3451 const QString retrieve = retrieveFromPrimitive(
3453 if (!retrieve.isEmpty())
3454 return retrieve;
3455 }
3456
3458 return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
3459 + castTargetName(to) + u">("_s + variable + u')';
3460 }
3461
3462 // Any value type is a non-null JS 'object' and therefore coerces to true.
3464 // All the interesting cases are already handled above:
3467 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
3468 Q_ASSERT(!isBoolOrNumber(from));
3469
3470 return u"true"_s;
3471 }
3472
3473 if (m_typeResolver->areEquivalentLists(from, to))
3474 return variable;
3475
3476 if (from->isListProperty()
3477 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
3478 && to->valueType()->isReferenceType()
3479 && !to->isListProperty()) {
3480 return variable + u".toList<"_s + to->internalName() + u">()"_s;
3481 }
3482
3483 bool isExtension = false;
3484 if (m_typeResolver->canPopulate(to, from, &isExtension)) {
3485 reject(u"populating "_s + to->internalName() + u" from "_s + from->internalName());
3486 } else if (const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
3487 ctor.isValid()) {
3488 const auto argumentTypes = ctor.parameters();
3489 return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
3490 + u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
3491 }
3492
3493 // TODO: add more conversions
3494
3495 reject(u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
3496 return QString();
3497}
3498
3500{
3501 const QQmlJSScope::ConstPtr containedFrom = m_typeResolver->containedType(from);
3502 const QQmlJSScope::ConstPtr containedTo = m_typeResolver->containedType(to);
3503
3504 // Those should be handled before, by convertStored().
3506 Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo));
3508 Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo));
3509
3512 reject(u"internal conversion into unsupported wrapper type."_s);
3513 }
3514
3515 bool isExtension = false;
3516 if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
3517 reject(u"populating "_s + containedTo->internalName()
3518 + u" from "_s + containedFrom->internalName());
3519 } else if (const auto ctor = m_typeResolver->selectConstructor(
3520 containedTo, containedFrom, &isExtension); ctor.isValid()) {
3521 const auto argumentTypes = ctor.parameters();
3522 const QQmlJSScope::ConstPtr argumentType = argumentTypes[0].type();
3523
3524 // We need to store the converted argument in a temporary
3525 // because it might not be an lvalue.
3526
3527 QString input;
3528 QString argPointer;
3529
3530 if (m_typeResolver->equals(argumentType, containedFrom)) {
3531 input = variable;
3532 argPointer = contentPointer(from, u"arg"_s);
3533 } else {
3538 argPointer = contentPointer(argument, u"arg"_s);
3539 }
3540
3541 return u"[&](){ auto arg = " + input
3542 + u"; return aotContext->constructValueType("_s + metaType(containedTo)
3543 + u", "_s + metaObject(
3544 isExtension ? containedTo->extensionType().scope : containedTo)
3545 + u", "_s + QString::number(int(ctor.constructorIndex()))
3546 + u", "_s + argPointer + u"); }()"_s;
3547 }
3548
3549 reject(u"internal conversion with incompatible or ambiguous types"_s);
3550 return QString();
3551}
3552
3554{
3555 setError(u"Cannot generate efficient code for %1"_s.arg(thing));
3556}
3557
3559 : accumulatorOut(generator->m_state.accumulatorOut())
3560 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
3561 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
3563{
3564 if (accumulatorVariableOut.isEmpty())
3565 return;
3566
3567 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
3568 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
3569 const QQmlJSScope::ConstPtr stored = accumulatorOut.storedType();
3570 const QQmlJSScope::ConstPtr origStored = resolver->originalType(stored);
3571
3572 // If the stored type differs or if we store in QVariant and the contained type differs,
3573 // then we have to use a temporary ...
3574 if (!resolver->equals(origStored, stored)
3575 || (!resolver->equals(origContained, resolver->containedType(accumulatorOut))
3576 && resolver->equals(stored, resolver->varType()))) {
3577
3578 const bool storable = isTypeStorable(resolver, origStored);
3579 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
3580 generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut));
3581 generator->m_body += u"{\n"_s;
3582 if (storable) {
3583 generator->m_body += origStored->augmentedInternalName() + u' '
3584 + generator->m_state.accumulatorVariableOut + u";\n";
3585 }
3586 } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
3587 && generator->m_state.readsRegister(Accumulator)
3588 && resolver->registerIsStoredIn(
3589 generator->m_state.accumulatorOut(), resolver->varType())) {
3590 // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
3591 // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
3592 // move the input out of the way first.
3593 generator->m_state.accumulatorVariableIn
3594 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
3595 generator->m_body += u"{\n"_s;
3596 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
3597 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
3598 }
3599}
3600
3602{
3603 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
3604 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
3605 generator->m_state.accumulatorOut(), accumulatorOut,
3606 generator->m_state.accumulatorVariableOut) + u";\n"_s;
3607 generator->m_body += u"}\n"_s;
3608 generator->m_state.setRegister(Accumulator, accumulatorOut);
3609 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
3610 } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
3611 generator->m_body += u"}\n"_s;
3612 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
3613 }
3614}
3615
3616
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool hasSeen(const T &s)
iterator end()
Definition qflatmap_p.h:773
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
\inmodule QtCore
Definition qhash.h:818
qsizetype size() const noexcept
Returns the number of items in the hash.
Definition qhash.h:925
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
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:1209
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
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_GetLookup(int index) override
void generate_StoreNameSloppy(int nameIndex) override
void generate_Shl(int lhs) override
QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext, const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger)
void generate_LoadName(int nameIndex) override
void generate_IteratorNext(int value, int done) 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_IteratorNextForYieldStar(int iterator, int object) 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
void generate_Decrement() override
void generate_Increment() override
void generate_CmpEqNull() override
void generate_DeleteProperty(int base, int index) 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, const InstructionAnnotations *annotations, QQmlJS::DiagnosticMessage *error)
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
void generate_BitAnd(int lhs) 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
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
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_IteratorClose(int done) 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
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
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
int value(const QString &key) const
bool isFlag() const
QString alias() const
QString name() const
bool hasValues() const
QQmlJSRegisterContent storedIn(const QQmlJSScope::ConstPtr &newStoredType) const
QQmlJSScope::ConstPtr scopeType() const
QList< QQmlJSScope::ConstPtr > conversionOrigins() 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
static QQmlJSScope::ConstPtr nonCompositeBaseType(const QQmlJSScope::ConstPtr &type)
AccessSemantics accessSemantics() const
bool isListProperty() const
QString augmentedInternalName() const
QQmlJSMetaProperty property(const QString &name) const
QQmlJSScope::ConstPtr baseType() 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
QQmlJSRegisterContent memberType(const QQmlJSRegisterContent &type, const QString &name) const
QQmlJSMetaMethod selectConstructor(const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &argument, bool *isExtension) const
QQmlJSScope::ConstPtr stringType() const
QQmlJSScope::ConstPtr listPropertyType() const
QQmlJSScope::ConstPtr nullType() const
QQmlJSScope::ConstPtr timeType() const
QQmlJSScope::ConstPtr genericType(const QQmlJSScope::ConstPtr &type, ComponentIsGeneric allowComponent=ComponentIsGeneric::No) 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 boolType() const
QQmlJSScope::ConstPtr qObjectListType() const
QQmlJSScope::ConstPtr jsGlobalObject() const
bool registerContains(const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) 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 jsValueType() const
QQmlJSRegisterContent globalType(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr originalType(const QQmlJSScope::ConstPtr &type) 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
bool isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
QQmlJSScope::ConstPtr int32Type() const
QQmlJSScope::ConstPtr voidType() const
QQmlJSScope::ConstPtr metaObjectType() const
QQmlJSScope::ConstPtr realType() 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) 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:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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:7822
QString & append(QChar c)
Definition qstring.cpp:3227
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)
static const QCssKnownValue origins[NumKnownOrigins - 1]
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:240
return ret
GLenum GLuint GLint level
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
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 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)
bool canCompareWithQObject(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canCompareWithQUrl(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
bool canStrictlyCompareWithVar(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
SSL_CTX int(*) void arg)
#define zero
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:29
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
#define decode(x)
const char property[13]
Definition qwizard.cpp:101
QRandomGenerator generator(sseq)
obj metaObject() -> className()
int originalValue
[1]
QDBusArgument argument
QJSValueList args
static QByteArray normalizedType(const char *type)
AccumulatorConverter(QQmlJSCodeGenerator *generator)
QList< QQmlJSRegisterContent > argumentTypes
QQmlJSScope::ConstPtr qmlScope
QQmlJSScope::ConstPtr returnType
const QQmlJSRegisterContent & accumulatorIn() const
bool canMoveReadRegister(int registerIndex) const
bool isRegisterAffectedBySideEffects(int registerIndex) const
QQmlJSRegisterContent readRegister(int registerIndex) const
const QQmlJSRegisterContent & changedRegister() const
QQmlJSRegisterContent readAccumulator() const
const QQmlJSRegisterContent & accumulatorOut() const
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)