11#include <private/qqmljstypepropagator_p.h>
13#include <private/qqmlirbuilder_p.h>
14#include <private/qqmljsscope_p.h>
15#include <private/qqmljsutils_p.h>
16#include <private/qv4compilerscanfunctions_p.h>
17#include <private/qduplicatetracker_p.h>
19#include <QtCore/qdir.h>
20#include <QtCore/qfileinfo.h>
24using namespace Qt::StringLiterals;
27
28
29
30
31
32
33
35#define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
37#define INJECT_TRACE_INFO(function)
38 static const bool injectTraceInfo = true;
39 if (injectTraceInfo) {
40 m_body += u"// "_s
+ QStringLiteral(#function) + u'\n'
;
46static bool isTypeStorable(
const QQmlJSTypeResolver *resolver,
const QQmlJSScope::ConstPtr &type)
48 return !type.isNull() && type != resolver->nullType() && type != resolver->voidType();
51QString QQmlJSCodeGenerator::castTargetName(
const QQmlJSScope::ConstPtr &type)
const
53 return type->augmentedInternalName();
56QQmlJSCodeGenerator::QQmlJSCodeGenerator(
57 const QV4::Compiler::Context *compilerContext,
58 const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver,
59 QQmlJSLogger *logger,
const BasicBlocks &basicBlocks,
60 const InstructionAnnotations &annotations,
bool noAotValidation)
61 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
62 , m_context(compilerContext)
63 , m_lookupSignaturesRecorder(logger->filePath(), typeResolver)
64 , m_noAotValidation(noAotValidation)
67QString QQmlJSCodeGenerator::metaTypeFromType(
const QQmlJSScope::ConstPtr &type)
const
69 return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
72QString QQmlJSCodeGenerator::metaTypeFromName(
const QQmlJSScope::ConstPtr &type)
const
74 return u"[]() { static constexpr QByteArrayView name(\""_s
75 + QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
76 + u"\"); static const auto t = QMetaType::fromName(name); return t; }()"_s;
79QString QQmlJSCodeGenerator::compositeListMetaType(
const QString &elementName)
const
81 return u"QQmlPrivate::compositeListMetaType(aotContext->compilationUnit, "_s
82 + (m_jsUnitGenerator->hasStringId(elementName)
83 ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
84 : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
87QString QQmlJSCodeGenerator::compositeMetaType(
const QString &elementName)
const
89 return u"QQmlPrivate::compositeMetaType(aotContext->compilationUnit, "_s
90 + (m_jsUnitGenerator->hasStringId(elementName)
91 ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
92 : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
95QString QQmlJSCodeGenerator::metaObject(
const QQmlJSScope::ConstPtr &objectType)
97 if (objectType->isComposite()) {
98 const QString name = m_typeResolver->nameForType(objectType);
101 u"retrieving the metaObject of a composite type without an element name."_s);
103 return compositeMetaType(name) + u".metaObject()"_s;
106 if (objectType->internalName() == u"QObject"_s
107 || objectType->internalName() == u"QQmlComponent"_s) {
108 return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
110 return metaTypeFromName(objectType) + u".metaObject()"_s;
113QString QQmlJSCodeGenerator::metaType(
const QQmlJSScope::ConstPtr &type)
115 if (type->isOpaqueType()) {
116 REJECT<QString>(
"Retrieving the metatype of an unregistered type"_L1);
118 if (type->isComposite()) {
119 const QString name = m_typeResolver->nameForType(type);
120 if (name.isEmpty()) {
122 u"retrieving the metaType of a composite type without an element name."_s);
124 return compositeMetaType(name);
127 if (type->isListProperty() && type->elementType()->isComposite()) {
128 const QString name = m_typeResolver->nameForType(type->elementType());
129 Q_ASSERT(!name.isEmpty());
130 return compositeListMetaType(name);
133 return (m_typeResolver->genericType(type) == type)
134 ? metaTypeFromType(type)
135 : metaTypeFromName(type);
148 return u"s.a%1"_s.arg(registerIndex - QQmlJSCompilePass::Argc);
149 if (registerIndex < 0)
150 return u"s.c%1_%2"_s.arg(-registerIndex).arg(offset);
151 return u"s.v%1_%2"_s.arg(registerIndex).arg(offset);
154QQmlJSAotFunction QQmlJSCodeGenerator::run(
const Function *function,
bool basicBlocksValidationFailed)
156 m_function = function;
158 if (m_context->contextType == QV4::Compiler::ContextType::Binding
159 && m_function->returnType.contains(m_typeResolver->qQmlScriptStringType())) {
160 const QString reason = u"binding for property of type QQmlScriptString; nothing to do."_s;
162 QQmlJSAotFunction result;
163 result.skipReason = reason;
167 const auto addVariable = [&](
int registerIndex,
int offset, QQmlJSRegisterContent content) {
168 const QQmlJSScope::ConstPtr seenType = content.storedType();
170 if (!isTypeStorable(m_typeResolver, seenType))
173 const auto oldSize = m_registerVariables.size();
174 auto &e = m_registerVariables[content];
175 if (m_registerVariables.size() != oldSize) {
176 e.variableName = registerName(registerIndex, offset);
177 e.storedType = seenType;
178 e.initialRegisterIndex = std::abs(registerIndex);
180 e.variableName += u'_' + registerName(registerIndex, offset).mid(2);
186QT_WARNING_DISABLE_CLANG(
"-Wrange-loop-analysis")
187 for (
auto it = m_annotations.constBegin(), iend = m_annotations.constEnd(); it != iend; ++it) {
188 const int registerIndex = it->second.changedRegisterIndex;
189 if (registerIndex != InvalidRegister)
190 addVariable(registerIndex, it.key(), it->second.changedRegister);
191 for (
auto jt = it->second.typeConversions.constBegin(),
192 jend = it->second.typeConversions.constEnd();
195 const int registerIndex = jt.key();
196 if (registerIndex != InvalidRegister)
197 addVariable(-registerIndex, it.key(), jt.value().content);
203 for (
const auto loopLabel : m_context->labelInfo)
204 m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
209 m_state.State::operator=(initialState(m_function));
211 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Temporary);
212 const QByteArray byteCode = function->code;
213 decode(byteCode.constData(),
static_cast<uint>(byteCode.size()));
214 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Permanent);
216 QQmlJSAotFunction result;
217 result.includes.swap(m_includes);
219 if (basicBlocksValidationFailed) {
220 result.code +=
"// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
221 "with basic blocks validation but compiled without it.\n"_L1;
224 result.code += u"// %1 at line %2, column %3\n"_s
225 .arg(m_context->name).arg(m_context->line).arg(m_context->column);
227 QStringList initializations;
228 QStringList markings;
229 for (
auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
230 registerIt != registerEnd; ++registerIt) {
233 QString declarationName = registerIt->variableName.mid(2);
235 const int registerIndex = registerIt->initialRegisterIndex;
236 const bool registerIsArgument = isArgument(registerIndex);
238 QString code = registerIt->storedType->internalName();
240 const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
242 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
248 if (!registerIsArgument
249 && registerIndex != Accumulator
250 && registerIndex != This
251 && !function->registerTypes[registerIndex - firstRegisterIndex()].contains(
252 m_typeResolver->voidType())) {
253 code += declarationName + u" = "_s;
254 code += convertStored(m_typeResolver->voidType(), storedType, QString());
255 }
else if (registerIsArgument && argumentType(registerIndex).isStoredIn(storedType)) {
256 const int argumentIndex = registerIndex - FirstArgument;
257 const QQmlJSRegisterContent argument
258 = m_function->argumentTypes[argumentIndex];
259 const QQmlJSRegisterContent originalArgument = originalType(argument);
261 const bool needsConversion = argument != originalArgument;
262 if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
272 code += declarationName + u" = "_s;
274 const auto originalContained = m_typeResolver->originalContainedType(argument);
275 QString originalValue;
276 const bool needsQVariantWrapping =
277 storedType->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
278 && !originalContained->isReferenceType()
279 && storedType == m_typeResolver->varType()
280 && originalContained != m_typeResolver->varType();
281 if (needsQVariantWrapping) {
282 originalValue = u"QVariant(%1, argv[%2])"_s.arg(metaTypeFromName(originalContained))
283 .arg(QString::number(argumentIndex + 1));
285 originalValue = u"(*static_cast<"_s + castTargetName(originalArgument.storedType())
286 + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
290 code += conversion(originalArgument, argument, originalValue);
292 code += originalValue;
293 }
else if (isPointer) {
294 code += declarationName + u" = nullptr"_s;
296 code += declarationName;
300 initializations.push_back(std::move(code));
303 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
304 }
else if (storedType == m_typeResolver->varType()) {
305 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
306 }
else if (storedType == m_typeResolver->listPropertyType()) {
308 }
else if (storedType == m_typeResolver->variantMapType()
309 || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
310 QString marking = u" for (const auto &v : std::as_const(" + declarationName + u"))\n"
311 + u" aotContext->mark(v, markStack);\n";
312 markings.append(std::move(marking));
316 result.code += u"struct Storage : QQmlPrivate::AOTTrackedLocalsStorage {\n"_s;
317 result.code += u"Storage(const QQmlPrivate::AOTCompiledContext *ctxt, void **a)"_s;
318 result.code += u" : aotContext(ctxt), argv(a) {}\n"_s;
319 result.code += u"void markObjects(QV4::MarkStack *markStack) const final {"_s;
320 result.code += u" Q_UNUSED(markStack);\n"_s;
323 for (
const QString &marking : std::as_const(markings))
324 result.code += marking;
326 result.code += u"}\n"_s;
327 result.code += u"const QQmlPrivate::AOTCompiledContext *aotContext;\n"_s;
328 result.code += u"void **argv;\n"_s;
331 initializations.sort();
332 for (
const QString &initialization : std::as_const(initializations))
333 result.code += initialization;
335 result.code += u"};\nStorage s(aotContext, argv);\n"_s;
336 result.code += u"aotContext->setLocals(&s);\n"_s;
338 result.code += m_body;
342 = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { contextUnit };\n"
343 " const auto *aotContext = &c;\n"
344 " Q_UNUSED(aotContext);\n"_s;
346 if (function->returnType.isValid()) {
347 signature += u" argTypes[0] = %1;\n"_s.arg(
348 metaType(function->returnType.containedType()));
350 signature += u" argTypes[0] = QMetaType();\n"_s;
352 result.numArguments = function->argumentTypes.length();
353 for (qsizetype i = 0; i != result.numArguments; ++i) {
354 signature += u" argTypes[%1] = %2;\n"_s.arg(
355 QString::number(i + 1),
356 metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
359 result.signature = std::move(signature);
363void QQmlJSCodeGenerator::generateReturnError()
365 const auto finalizeReturn = qScopeGuard([
this]() { m_body += u"return;\n"_s; });
367 m_body += u"aotContext->setReturnValueUndefined();\n"_s;
368 const auto ret = m_function->returnType;
369 if (!ret.isValid() || ret.contains(m_typeResolver->voidType()))
372 m_body += u"if (argv[0]) {\n"_s;
374 const auto contained = ret.containedType();
375 const auto stored = ret.storedType();
376 if (contained->isReferenceType() && stored->isReferenceType()) {
377 m_body += u" *static_cast<"_s
378 + stored->augmentedInternalName()
379 + u" *>(argv[0]) = nullptr;\n"_s;
380 }
else if (contained == stored) {
381 m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
382 + stored->internalName() + u"();\n"_s;
384 m_body += u" const QMetaType returnType = "_s
385 + metaType(ret.containedType()) + u";\n"_s;
386 m_body += u" returnType.destruct(argv[0]);\n"_s;
387 m_body += u" returnType.construct(argv[0]);\n "_s;
393void QQmlJSCodeGenerator::generate_Ret()
397 const auto finalizeReturn = qScopeGuard([
this]() {
398 m_body += u"return;\n"_s;
399 m_skipUntilNextLabel =
true;
403 if (!m_function->returnType.isValid())
406 m_body += u"if (argv[0]) {\n"_s;
408 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
409 const QString in = m_state.accumulatorVariableIn;
411 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
414 if (accumulatorIn.isStoredIn(m_typeResolver->voidType()))
415 m_body += signalUndefined;
416 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
417 m_body += u" if (!"_s + in + u".isValid())\n"_s;
418 m_body += u" "_s + signalUndefined;
419 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
420 m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
421 m_body += u" "_s + signalUndefined;
422 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
423 m_body += u" if ("_s + in + u".isUndefined())\n"_s;
424 m_body += u" "_s + signalUndefined;
427 if (m_function->returnType.contains(m_typeResolver->voidType())) {
432 const auto contained = m_function->returnType.containedType();
433 const auto stored = m_function->returnType.storedType();
434 if (contained == stored || (contained->isReferenceType() && stored->isReferenceType())) {
437 m_body += u" *static_cast<"_s
438 + stored->augmentedInternalName()
439 + u" *>(argv[0]) = "_s
441 accumulatorIn, m_function->returnType,
442 m_typeResolver->isTriviallyCopyable(accumulatorIn.storedType())
444 : u"std::move("_s + in + u')')
446 }
else if (accumulatorIn.contains(contained)) {
447 m_body += u" const QMetaType returnType = "_s + contentType(accumulatorIn, in)
449 m_body += u" returnType.destruct(argv[0]);\n"_s;
450 m_body += u" returnType.construct(argv[0], "_s
451 + contentPointer(accumulatorIn, in) + u");\n"_s;
453 m_body += u" const auto converted = "_s
454 + conversion(accumulatorIn, m_function->returnType,
455 consumedAccumulatorVariableIn()) + u";\n"_s;
456 m_body += u" const QMetaType returnType = "_s
457 + contentType(m_function->returnType, u"converted"_s)
459 m_body += u" returnType.destruct(argv[0]);\n"_s;
460 m_body += u" returnType.construct(argv[0], "_s
461 + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
467void QQmlJSCodeGenerator::generate_Debug()
474 if (value >= std::numeric_limits<
int>::min() && value <= std::numeric_limits<
int>::max()) {
477 return QString::number(i);
480 switch (qFpClassify(value)) {
482 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
483 return std::signbit(value) ? (u'-' + inf) : inf;
486 return u"std::numeric_limits<double>::quiet_NaN()"_s;
488 return std::signbit(value) ? u"-0.0"_s : u"0"_s;
493 return QString::number(value,
'f', std::numeric_limits<
double>::max_digits10);
496void QQmlJSCodeGenerator::generate_LoadConst(
int index)
504 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
505 const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst);
506 const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst);
508 m_body += m_state.accumulatorVariableOut + u" = "_s;
509 if (type == m_typeResolver->realType()) {
510 m_body += conversion(
511 type, m_state.accumulatorOut(),
512 toNumericString(value.doubleValue()));
513 }
else if (type == m_typeResolver->int32Type()) {
514 m_body += conversion(
515 type, m_state.accumulatorOut(),
516 QString::number(value.integerValue()));
517 }
else if (type == m_typeResolver->boolType()) {
518 m_body += conversion(
519 type, m_state.accumulatorOut(),
520 value.booleanValue() ? u"true"_s : u"false"_s);
521 }
else if (type == m_typeResolver->voidType()) {
522 m_body += conversion(
523 type, m_state.accumulatorOut(),
525 }
else if (type == m_typeResolver->nullType()) {
526 m_body += conversion(
527 type, m_state.accumulatorOut(),
530 REJECT(u"unsupported constant type"_s);
536void QQmlJSCodeGenerator::generate_LoadZero()
540 m_body += m_state.accumulatorVariableOut;
541 m_body += u" = "_s + conversion(
542 m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
546void QQmlJSCodeGenerator::generate_LoadTrue()
550 m_body += m_state.accumulatorVariableOut;
551 m_body += u" = "_s + conversion(
552 m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
556void QQmlJSCodeGenerator::generate_LoadFalse()
560 m_body += m_state.accumulatorVariableOut;
561 m_body += u" = "_s + conversion(
562 m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
566void QQmlJSCodeGenerator::generate_LoadNull()
570 m_body += m_state.accumulatorVariableOut + u" = "_s;
571 m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
576void QQmlJSCodeGenerator::generate_LoadUndefined()
580 m_body += m_state.accumulatorVariableOut + u" = "_s;
581 m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
586void QQmlJSCodeGenerator::generate_LoadInt(
int value)
590 m_body += m_state.accumulatorVariableOut;
592 m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
593 QString::number(value));
597void QQmlJSCodeGenerator::generate_MoveConst(
int constIndex,
int destTemp)
601 Q_ASSERT(destTemp == m_state.changedRegisterIndex());
603 auto var = changedRegisterVariable();
607 const auto v4Value = QV4::StaticValue::fromReturnedValue(
608 m_jsUnitGenerator->constant(constIndex));
610 const auto changed = m_state.changedRegister();
611 QQmlJSScope::ConstPtr contained;
614 m_body += var + u" = "_s;
615 if (v4Value.isNull()) {
616 contained = m_typeResolver->nullType();
617 }
else if (v4Value.isUndefined()) {
618 contained = m_typeResolver->voidType();
619 }
else if (v4Value.isBoolean()) {
620 contained = m_typeResolver->boolType();
621 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
622 }
else if (v4Value.isInteger()) {
623 contained = m_typeResolver->int32Type();
624 input = QString::number(v4Value.int_32());
625 }
else if (v4Value.isDouble()) {
626 contained = m_typeResolver->realType();
627 input = toNumericString(v4Value.doubleValue());
629 REJECT(u"unknown const type"_s);
631 m_body += conversion(contained, changed, input) + u";\n"_s;
634void QQmlJSCodeGenerator::generate_LoadReg(
int reg)
638 if (registerVariable(reg) == m_state.accumulatorVariableOut)
641 m_body += m_state.accumulatorVariableOut;
643 m_body += conversion(
644 registerType(reg), m_state.accumulatorOut(), consumedRegisterVariable(reg));
648void QQmlJSCodeGenerator::generate_StoreReg(
int reg)
652 Q_ASSERT(m_state.changedRegisterIndex() == reg);
653 Q_ASSERT(m_state.accumulatorIn().isValid());
654 const QString var = changedRegisterVariable();
657 if (var == m_state.accumulatorVariableIn)
661 m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
662 consumedAccumulatorVariableIn());
666void QQmlJSCodeGenerator::generate_MoveReg(
int srcReg,
int destReg)
670 Q_ASSERT(m_state.changedRegisterIndex() == destReg);
671 const QString destRegName = changedRegisterVariable();
672 if (destRegName.isEmpty())
674 if (destRegName == registerVariable(srcReg))
676 m_body += destRegName;
678 m_body += conversion(
679 registerType(srcReg), m_state.changedRegister(), consumedRegisterVariable(srcReg));
683void QQmlJSCodeGenerator::generate_LoadImport(
int index)
689void QQmlJSCodeGenerator::generate_LoadLocal(
int index)
695void QQmlJSCodeGenerator::generate_StoreLocal(
int index)
701void QQmlJSCodeGenerator::generate_LoadScopedLocal(
int scope,
int index)
708void QQmlJSCodeGenerator::generate_StoreScopedLocal(
int scope,
int index)
715void QQmlJSCodeGenerator::generate_LoadRuntimeString(
int stringId)
719 m_body += m_state.accumulatorVariableOut;
721 m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
722 QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
726void QQmlJSCodeGenerator::generate_MoveRegExp(
int regExpId,
int destReg)
733void QQmlJSCodeGenerator::generate_LoadClosure(
int value)
739void QQmlJSCodeGenerator::generate_LoadName(
int nameIndex)
745void QQmlJSCodeGenerator::generate_LoadGlobalLookup(
int index)
749 AccumulatorConverter registers(
this);
751 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index) + u", "_s
752 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
753 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
754 + QString::number(index) + u", "_s
755 + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
756 const QString preparation = getLookupPreparation(
757 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
758 generateLookup(lookup, initialization, preparation);
761void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(
int index)
765 AccumulatorConverter registers(
this);
767 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
769 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
770 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
773 m_body += m_state.accumulatorVariableOut + u" = "_s
775 m_typeResolver->jsValueType(), m_state.accumulatorOut(),
776 u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
781 const QString indexString = QString::number(index);
782 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
783 const QString lookup = u"aotContext->loadContextIdLookup("_s
784 + indexString + u", "_s
785 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
786 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
787 + indexString + u')';
788 generateLookup(lookup, initialization);
792 const bool isProperty = m_state.accumulatorOut().isProperty();
794 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
795 + indexString + u", "_s
796 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
797 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
798 + indexString + u')';
799 const QString preparation = getLookupPreparation(
800 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
802 recordPropertyLookup(m_typeResolver->original(m_state.accumulatorOut()).scopeType(),
803 originalProperty(m_state.accumulatorOut()));
804 generateLookup(lookup, initialization, preparation);
805 }
else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
806 generateTypeLookup(index);
808 REJECT(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
812void QQmlJSCodeGenerator::generate_StoreNameSloppy(
int nameIndex)
816 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
817 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
818 Q_ASSERT(type.isProperty());
820 switch (type.variant()) {
821 case QQmlJSRegisterContent::Property: {
824 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
826 + contentPointer(m_state.accumulatorIn(), m_state.accumulatorVariableIn)
828 + contentType(m_state.accumulatorIn(), m_state.accumulatorVariableIn) + u')';
832 case QQmlJSRegisterContent::Method:
833 REJECT(u"assignment to scope method"_s);
839void QQmlJSCodeGenerator::generate_StoreNameStrict(
int name)
845void QQmlJSCodeGenerator::generate_LoadElement(
int base)
849 const QQmlJSRegisterContent baseType = registerType(base);
851 if (!baseType.isList() && !baseType.isStoredIn(m_typeResolver->stringType()))
852 REJECT(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
854 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
855 conversion(literalType(m_typeResolver->voidType()),
856 m_state.accumulatorOut(), QString()) + u";\n"_s;
858 AccumulatorConverter registers(
this);
860 QString indexName = m_state.accumulatorVariableIn;
861 QQmlJSScope::ConstPtr indexType;
862 if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
863 indexType = m_state.accumulatorIn().containedType();
864 }
else if (m_state.accumulatorIn().isConversion()) {
865 const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
866 if (m_typeResolver->isNumeric(target)) {
867 indexType = target.containedType();
868 m_body += u"if (!" + indexName + u".metaType().isValid())\n"
871 indexName = convertStored(
872 m_state.accumulatorIn().storedType(), indexType, indexName);
874 REJECT(u"LoadElement with non-numeric argument"_s);
878 const QString baseName = registerVariable(base);
880 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
881 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
884 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
885 m_body += u"if ("_s + indexName + u" < 0)\n"_s
890 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
892 m_body += u"if ("_s + indexName + u" < "_s + baseName
893 + u".count(&"_s + baseName + u"))\n"_s;
894 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
895 conversion(m_typeResolver->qObjectType(), m_state.accumulatorOut(),
896 baseName + u".at(&"_s + baseName + u", "_s
897 + indexName + u')') + u";\n"_s;
898 m_body += u"else\n"_s
904 QQmlJSRegisterContent elementType = m_typeResolver->elementType(baseType);
905 elementType = m_pool->storedIn(
906 elementType, m_typeResolver->storedType(elementType.containedType()));
908 QString access = baseName + u".at("_s + indexName + u')';
911 if (baseType.isStoredIn(m_typeResolver->stringType()))
912 access = u"QString("_s + access + u")"_s;
913 else if (isRegisterAffectedBySideEffects(base))
914 REJECT(u"LoadElement on a sequence potentially affected by side effects"_s);
915 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
916 REJECT(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
918 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
919 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
920 conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
921 m_body += u"else\n"_s
925void QQmlJSCodeGenerator::generate_StoreElement(
int base,
int index)
929 const QQmlJSRegisterContent baseType = registerType(base);
930 const QQmlJSScope::ConstPtr indexType = registerType(index).containedType();
932 if (!m_typeResolver->isNumeric(indexType) || !baseType.isList())
933 REJECT(u"StoreElement with non-list base type or non-numeric arguments"_s);
935 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
936 REJECT(u"indirect StoreElement"_s);
938 const QString baseName = registerVariable(base);
939 const QString indexName = registerVariable(index);
941 const auto elementType = m_typeResolver->genericType(
942 m_typeResolver->elementType(baseType).containedType());
944 addInclude(u"QtQml/qjslist.h"_s);
945 if (!m_typeResolver->isNativeArrayIndex(indexType))
946 m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
947 else if (!m_typeResolver->isUnsignedInteger(indexType))
948 m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
952 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
953 m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
955 m_body += u" "_s + baseName + u".replace(&"_s + baseName
956 + u", "_s + indexName + u", "_s;
957 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
963 if (isRegisterAffectedBySideEffects(base))
964 REJECT(u"StoreElement on a sequence potentially affected by side effects"_s);
965 if (isRegisterAffectedBySideEffects(Accumulator))
966 REJECT(u"StoreElement of a value potentially affected by side effects"_s);
968 m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
969 m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
970 + indexName + u" + 1);\n"_s;
971 m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
972 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
976 generateWriteBack(base);
979void QQmlJSCodeGenerator::generate_LoadProperty(
int nameIndex)
982 REJECT(u"LoadProperty"_s);
985void QQmlJSCodeGenerator::generate_LoadOptionalProperty(
int name,
int offset)
992void QQmlJSCodeGenerator::generateEnumLookup(
int index)
994 const QString enumMember = m_state.accumulatorOut().enumMember();
1001 if (enumMember.isEmpty())
1002 REJECT(u"Lookup of enum metatype"_s);
1005 const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
1006 if (metaEnum.hasValues()) {
1007 m_body += m_state.accumulatorVariableOut + u" = "_s
1008 + QString::number(metaEnum.value(enumMember));
1010 const QQmlJSRegisterContent &scope = m_state.accumulatorOut().scope();
1011 recordEnumKeyLookup(m_typeResolver->originalContainedType(scope), metaEnum, enumMember);
1015 const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
1018 Q_ASSERT(!scopeType->isComposite());
1020 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
1021 if (enumName.isEmpty()) {
1022 if (metaEnum.isFlag() && !metaEnum.name().isEmpty()) {
1023 REJECT(u"qmltypes misses name entry for flag; "
1024 "did you pass the enum type to Q_FLAG instead of the QFlag type?"
1025 "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
1027 REJECT(u"qmltypes misses name entry for enum"_s);
1029 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
1030 + u", &"_s + m_state.accumulatorVariableOut + u')';
1031 const QString initialization = u"aotContext->initGetEnumLookup("_s
1032 + QString::number(index) + u", "_s + metaObject(scopeType)
1033 + u", \""_s + enumName + u"\", \""_s + enumMember
1037 generateLookup(lookup, initialization);
1040void QQmlJSCodeGenerator::generateTypeLookup(
int index)
1042 const QString indexString = QString::number(index);
1043 const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator).content;
1044 const QString namespaceString
1045 = accumulatorIn.isImportNamespace()
1046 ? QString::number(accumulatorIn.importNamespace())
1047 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1049 switch (m_state.accumulatorOut().variant()) {
1050 case QQmlJSRegisterContent::Singleton: {
1051 rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
1052 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
1053 + u", &"_s + m_state.accumulatorVariableOut + u')';
1054 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
1055 + u", "_s + namespaceString + u')';
1056 generateLookup(lookup, initialization);
1059 case QQmlJSRegisterContent::ModulePrefix:
1061 case QQmlJSRegisterContent::Attachment: {
1062 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1063 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1064 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
1065 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
1066 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
1067 generateLookup(lookup, initialization);
1070 case QQmlJSRegisterContent::Script:
1071 REJECT(u"script lookup"_s);
1072 case QQmlJSRegisterContent::MetaType: {
1073 if (!m_state.accumulatorOut().isStoredIn(m_typeResolver->metaObjectType())) {
1076 REJECT(u"meta-object stored in different type"_s);
1078 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
1079 + u", &"_s + m_state.accumulatorVariableOut + u')';
1080 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
1081 + u", "_s + namespaceString + u")"_s;
1082 generateLookup(lookup, initialization);
1090void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1091 QQmlJSRegisterContent nonStorableContent,
const QString ®isterName,
bool invert)
1093 const auto nonStorableType = nonStorableContent.containedType();
1094 QQmlJSScope::ConstPtr comparedType = (nonStorableType == m_typeResolver->nullType())
1095 ? m_typeResolver->nullType()
1096 : m_typeResolver->voidType();
1099 m_body += u"if ("_s + registerName
1100 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
1101 + m_state.accumulatorVariableOut + u" = "_s
1102 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1103 u"static_cast<const QJSPrimitiveValue *>("_s + registerName
1104 + u".constData())"_s + u"->type() "_s
1105 + (invert ? u"!="_s : u"=="_s)
1106 + (comparedType == m_typeResolver->nullType()
1107 ? u"QJSPrimitiveValue::Null"_s
1108 : u"QJSPrimitiveValue::Undefined"_s))
1109 + u";\n} else if ("_s + registerName
1110 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
1111 + m_state.accumulatorVariableOut + u" = "_s
1112 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1113 (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
1114 + registerName + u".constData())"_s + u"->"_s
1115 + (comparedType == m_typeResolver->nullType()
1117 : u"isUndefined()"_s))
1122 if (nonStorableType == m_typeResolver->nullType()) {
1123 m_body += u"else if ("_s + registerName
1124 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
1125 + m_state.accumulatorVariableOut + u" = "_s
1126 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1127 u"*static_cast<QObject *const *>("_s + registerName
1128 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
1130 + u";\n} else if ("_s + registerName
1131 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
1132 + m_state.accumulatorVariableOut + u" = "_s
1133 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1134 (invert ? u"false"_s : u"true"_s))
1139 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
1140 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1141 (invert ? (registerName + u".isValid() ? true : false"_s)
1142 : (registerName + u".isValid() ? false : true"_s)))
1146void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1147 QQmlJSRegisterContent storableContent,
const QString &typedRegisterName,
1148 const QString &varRegisterName,
bool invert)
1152 const QQmlJSScope::ConstPtr contained = storableContent.isEnumeration()
1153 ? storableContent.storedType()
1154 : storableContent.containedType();
1156 const QQmlJSScope::ConstPtr boolType = m_typeResolver->boolType();
1157 if (contained->isReferenceType()) {
1158 const QQmlJSScope::ConstPtr comparable = m_typeResolver->qObjectType();
1159 const QString cmpExpr = (invert ? u"!"_s : QString()) + u"(("
1160 + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
1161 + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
1162 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1164 m_body += m_state.accumulatorVariableOut + u" = "_s
1165 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n";
1169 if (m_typeResolver->isPrimitive(contained)) {
1170 const QQmlJSScope::ConstPtr comparable = m_typeResolver->jsPrimitiveType();
1171 const QString cmpExpr = (invert ? u"!"_s : QString())
1172 + conversion(storableContent, comparable, typedRegisterName)
1173 + u".strictlyEquals("_s
1174 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1176 m_body += m_state.accumulatorVariableOut + u" = "_s
1177 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n"_s;
1181 REJECT(u"comparison of non-primitive, non-object type to var"_s);
1184void QQmlJSCodeGenerator::generateArrayInitializer(
int argc,
int argv)
1186 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1187 const QQmlJSScope::ConstPtr element = stored->elementType();
1190 QStringList initializer;
1191 for (
int i = 0; i < argc; ++i) {
1192 initializer += convertStored(
1193 registerType(argv + i).storedType(), element,
1194 consumedRegisterVariable(argv + i));
1197 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
1198 m_body += initializer.join(u", "_s);
1202void QQmlJSCodeGenerator::generateWriteBack(
int registerIndex)
1204 QString writeBackRegister = registerVariable(registerIndex);
1205 bool writeBackAffectedBySideEffects = isRegisterAffectedBySideEffects(registerIndex);
1207 for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
1208 !writeBack.storedType()->isReferenceType();) {
1210 switch (writeBack.variant()) {
1211 case QQmlJSRegisterContent::Literal:
1212 case QQmlJSRegisterContent::Operation:
1220 if (writeBackAffectedBySideEffects)
1221 REJECT(u"write-back of value affected by side effects"_s);
1223 if (writeBack.isConversion())
1224 REJECT(u"write-back of converted value"_s);
1226 const int lookupIndex = writeBack.resultLookupIndex();
1237 if (lookupIndex == -1)
1238 REJECT(u"write-back of non-lookup"_s);
1240 const QString writeBackIndexString = QString::number(lookupIndex);
1242 const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
1243 if (variant == QQmlJSRegisterContent::Property && isQmlScopeObject(writeBack.scope())) {
1244 const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
1245 + writeBackIndexString
1246 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1247 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
1248 + writeBackIndexString + u')';
1249 recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()),
1250 originalProperty(writeBack));
1251 generateLookup(lookup, initialization);
1256 QQmlJSRegisterContent outerContent;
1257 QString outerRegister;
1258 bool outerAffectedBySideEffects =
false;
1259 for (
auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
1261 if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
1262 outerContent = it.value().content;
1263 outerRegister = lookupVariable(outerContent.resultLookupIndex());
1264 outerAffectedBySideEffects = it.value().affectedBySideEffects;
1270 if (!outerContent.isValid())
1271 REJECT(u"write-back of lookup across jumps or merges."_s);
1273 Q_ASSERT(!outerRegister.isEmpty());
1275 switch (writeBack.variant()) {
1276 case QQmlJSRegisterContent::Property: {
1277 const QQmlJSMetaProperty property = originalProperty(writeBack);
1278 if (writeBack.scopeType()->isReferenceType()) {
1279 const QString lookup = u"aotContext->writeBackObjectLookup("_s
1280 + writeBackIndexString
1281 + u", "_s + outerRegister
1282 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1284 const QString initialization = (m_state.registers[registerIndex].isShadowable
1285 ? u"aotContext->initGetObjectLookupAsVariant("_s
1286 : u"aotContext->initGetObjectLookup("_s)
1287 + writeBackIndexString + u", "_s + outerRegister + u')';
1288 recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()), property);
1289 generateLookup(lookup, initialization);
1291 const QString valuePointer = contentPointer(outerContent, outerRegister);
1292 const QString lookup = u"aotContext->writeBackValueLookup("_s
1293 + writeBackIndexString
1294 + u", "_s + valuePointer
1295 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1296 const QString initialization = u"aotContext->initGetValueLookup("_s
1297 + writeBackIndexString
1298 + u", "_s + metaObject(writeBack.scopeType()) + u')';
1299 recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()), property);
1300 generateLookup(lookup, initialization);
1305 REJECT(u"SetLookup on value types (because of missing write-back)"_s);
1308 writeBackRegister = outerRegister;
1309 writeBack = outerContent;
1310 writeBackAffectedBySideEffects = outerAffectedBySideEffects;
1314void QQmlJSCodeGenerator::rejectIfNonQObjectOut(
const QString &error)
1316 if (m_state.accumulatorOut().storedType()->accessSemantics()
1317 != QQmlJSScope::AccessSemantics::Reference) {
1322void QQmlJSCodeGenerator::rejectIfBadArray()
1324 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1325 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
1329 REJECT(u"storing an array in a non-sequence type"_s);
1330 }
else if (stored->isListProperty()) {
1339 REJECT(u"creating a QQmlListProperty not backed by a property"_s);
1345
1346
1347
1348
1349
1350
1351bool QQmlJSCodeGenerator::generateContentPointerCheck(
1352 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1353 const QString &variable,
const QString &errorMessage)
1355 const QQmlJSScope::ConstPtr scope = required;
1356 const QQmlJSScope::ConstPtr input = actual.containedType();
1357 if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
1358 [&](
const QQmlJSScope::ConstPtr &base) {
return base == scope; })) {
1362 if (!m_typeResolver->canHold(input, scope)) {
1363 REJECT<
bool>(u"lookup of members of %1 in %2"_s
1364 .arg(scope->internalName(), input->internalName()));
1367 bool needsVarContentConversion =
false;
1368 QString processedErrorMessage;
1369 if (actual.storedType()->isReferenceType()) {
1373 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
1374 processedErrorMessage = errorMessage.arg(u"null");
1375 }
else if (actual.isStoredIn(m_typeResolver->varType())) {
1379 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
1380 needsVarContentConversion =
true;
1381 processedErrorMessage = errorMessage.arg(u"undefined");
1383 REJECT<
bool>(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
1386 generateSetInstructionPointer();
1387 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
1388 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
1389 generateReturnError();
1391 return needsVarContentConversion;
1394QString QQmlJSCodeGenerator::generateCallConstructor(
1395 const QQmlJSMetaMethod &ctor,
const QList<QQmlJSRegisterContent> &argumentTypes,
1396 const QStringList &arguments,
const QString &metaType,
const QString &metaObject)
1398 const auto parameterTypes = ctor.parameters();
1399 Q_ASSERT(parameterTypes.length() == argumentTypes.length());
1402 QStringList argPointers;
1404 QString result = u"[&](){\n"_s;
1405 for (qsizetype i = 0, end = parameterTypes.length(); i < end; ++i) {
1406 const QQmlJSRegisterContent argumentType = argumentTypes[i];
1407 const QQmlJSScope::ConstPtr parameterType = parameterTypes[i].type();
1408 const QString argument = arguments[i];
1409 const QString arg = u"arg"_s + QString::number(i);
1411 result += u" auto "_s + arg + u" = "_s;
1412 if (argumentType.contains(parameterType)) {
1414 argPointers.append(contentPointer(argumentType, arg));
1416 const QQmlJSRegisterContent parameterTypeConversion
1418 m_typeResolver->convert(argumentType, parameterType),
1419 m_typeResolver->genericType(parameterType));
1420 result += conversion(argumentType, parameterTypeConversion, argument);
1421 argPointers.append(contentPointer(parameterTypeConversion, arg));
1426 result += u" void *args[] = {"_s + argPointers.join(u',') + u"};\n"_s;
1427 result += u" return aotContext->constructValueType("_s + metaType + u", "_s + metaObject
1428 + u", "_s + QString::number(
int(ctor.constructorIndex())) + u", args);\n"_s;
1430 return result + u"}()"_s;
1434 const QQmlJSTypeResolver *typeResolver,
const QQmlJSRegisterContent &baseType)
1436 const QQmlJSScope::ConstPtr contained = baseType.containedType();
1437 switch (contained->accessSemantics()) {
1446 return !typeResolver->isPrimitive(contained);
1449 if (contained->isListProperty())
1452 switch (baseType.variant()) {
1453 case QQmlJSRegisterContent::Operation:
1454 case QQmlJSRegisterContent::Literal: {
1456 const QQmlJSScope::ConstPtr elementContained = contained->elementType();
1457 return !elementContained->isReferenceType()
1458 && !typeResolver->isPrimitive(elementContained);
1468bool QQmlJSCodeGenerator::isRegisterAffectedBySideEffects(
int registerIndex)
1470 if (!m_state.isRegisterAffectedBySideEffects(registerIndex))
1473 QQmlJSRegisterContent baseType = registerType(registerIndex);
1474 if (baseType.isConversion()) {
1478 const auto &origins = baseType.conversionOrigins();
1479 for (QQmlJSRegisterContent origin : origins) {
1480 if (canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(origin)))
1487 return canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(baseType));
1490QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
1491 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1492 const QString &variable,
const QString &errorMessage)
1494 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1495 return variable + u".data()"_s;
1496 return contentPointer(actual, variable);
1499QString QQmlJSCodeGenerator::resolveQObjectPointer(
1500 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1501 const QString &variable,
const QString &errorMessage)
1503 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1504 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1508void QQmlJSCodeGenerator::generate_GetLookup(
int index)
1511 generate_GetLookupHelper(index);
1514QString QQmlJSCodeGenerator::generateVariantMapGetLookup(
1515 const QString &map,
const int nameIndex)
1517 const QString mapLookup = map
1518 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1520 return m_state.accumulatorVariableOut + u" = "_s
1521 + conversion(m_typeResolver->varType(), m_state.accumulatorOut(), mapLookup)
1525QString QQmlJSCodeGenerator::generateVariantMapSetLookup(
1526 const QString &map,
const int nameIndex,
1527 const QQmlJSScope::ConstPtr &property,
const QString &variableIn)
1529 const QString mapLookup = map
1530 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1532 return mapLookup + u" = "_s
1533 + conversion(property, m_typeResolver->varType(), variableIn)
1537void QQmlJSCodeGenerator::generate_GetLookupHelper(
int index)
1539 if (m_state.accumulatorOut().isMethod())
1540 REJECT(u"lookup of function property."_s);
1542 if (m_state.accumulatorOut().scope().contains(m_typeResolver->mathObject())) {
1543 QString name = m_jsUnitGenerator->lookupName(index);
1547 value = std::exp(1.0);
1548 }
else if (name == u"LN10") {
1550 }
else if (name == u"LN2") {
1552 }
else if (name == u"LOG10E") {
1553 value = log10(std::exp(1.0));
1554 }
else if (name == u"LOG2E") {
1555 value = log2(std::exp(1.0));
1556 }
else if (name == u"PI") {
1557 value = 3.14159265358979323846;
1558 }
else if (name == u"SQRT1_2") {
1559 value = std::sqrt(0.5);
1560 }
else if (name == u"SQRT2") {
1561 value = std::sqrt(2.0);
1566 m_body += m_state.accumulatorVariableOut + u" = "_s
1567 + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value))
1572 if (m_state.accumulatorOut().isImportNamespace()) {
1573 Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ModulePrefix);
1575 if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
1576 m_body += m_state.accumulatorVariableOut + u" = "_s
1577 + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
1578 m_state.accumulatorVariableIn)
1584 AccumulatorConverter registers(
this);
1586 if (m_state.accumulatorOut().isEnumeration()) {
1587 generateEnumLookup(index);
1591 const QString indexString = QString::number(index);
1592 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1593 ? QString::number(m_state.accumulatorIn().importNamespace())
1594 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1595 const auto accumulatorIn = m_state.accumulatorIn();
1596 const QQmlJSRegisterContent scope = m_state.accumulatorOut().scope();
1597 const QQmlJSRegisterContent originalScope
1598 = scope.original().isValid() ? scope.original() : scope;
1599 const bool isReferenceType = originalScope.containedType()->isReferenceType();
1601 switch (m_state.accumulatorOut().variant()) {
1602 case QQmlJSRegisterContent::Attachment: {
1603 if (isQmlScopeObject(m_state.accumulatorOut().attachee())) {
1604 generateTypeLookup(index);
1607 if (!isReferenceType) {
1612 REJECT(u"attached object for non-QObject type"_s);
1615 if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
1618 REJECT(u"attached object of potentially non-QObject base"_s);
1621 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1623 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1624 + u", "_s + m_state.accumulatorVariableIn
1625 + u", &"_s + m_state.accumulatorVariableOut + u')';
1626 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1627 + indexString + u", "_s + namespaceString + u", "_s
1628 + m_state.accumulatorVariableIn + u')';
1629 generateLookup(lookup, initialization);
1632 case QQmlJSRegisterContent::Singleton:
1633 case QQmlJSRegisterContent::Script:
1634 case QQmlJSRegisterContent::MetaType: {
1635 generateTypeLookup(index);
1642 Q_ASSERT(m_state.accumulatorOut().isProperty());
1644 if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1645 REJECT(u"lookup in QJSValue"_s);
1646 }
else if (isReferenceType) {
1647 const QString inputPointer = resolveQObjectPointer(
1648 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1649 u"Cannot read property '%1' of %2"_s.arg(
1650 m_jsUnitGenerator->lookupName(index)));
1651 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1652 + u", "_s + inputPointer + u", "_s
1653 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
1654 const QString initialization = (m_state.isShadowable()
1655 ? u"aotContext->initGetObjectLookupAsVariant("_s
1656 : u"aotContext->initGetObjectLookup("_s)
1657 + indexString + u", "_s + inputPointer + u')';
1658 const QString preparation = getLookupPreparation(
1659 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1660 recordPropertyLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
1661 originalProperty(m_state.accumulatorOut()));
1662 generateLookup(lookup, initialization, preparation);
1663 }
else if ((originalScope.containedType()->accessSemantics()
1664 == QQmlJSScope::AccessSemantics::Sequence
1665 || originalScope.contains(m_typeResolver->stringType()))
1666 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1667 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1668 if (stored->isListProperty()) {
1669 m_body += m_state.accumulatorVariableOut + u" = "_s;
1670 m_body += conversion(
1671 originalType(m_state.accumulatorOut()),
1672 m_state.accumulatorOut(),
1673 m_state.accumulatorVariableIn + u".count("_s + u'&'
1674 + m_state.accumulatorVariableIn + u')');
1676 }
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1677 || stored == m_typeResolver->stringType()) {
1678 m_body += m_state.accumulatorVariableOut + u" = "_s
1679 + conversion(originalType(m_state.accumulatorOut()),
1680 m_state.accumulatorOut(),
1681 m_state.accumulatorVariableIn + u".length()"_s)
1683 }
else if (originalScope.contains(m_typeResolver->stringType())) {
1684 m_body += m_state.accumulatorVariableOut + u" = "_s
1686 m_typeResolver->sizeType(), m_state.accumulatorOut(),
1687 conversion(m_state.accumulatorIn(), m_typeResolver->stringType(),
1688 m_state.accumulatorVariableIn)
1692 REJECT(u"access to 'length' property of sequence wrapped in non-sequence"_s);
1694 }
else if (accumulatorIn.isStoredIn(m_typeResolver->variantMapType())) {
1695 m_body += generateVariantMapGetLookup(m_state.accumulatorVariableIn, index);
1697 if (m_state.isRegisterAffectedBySideEffects(Accumulator))
1698 REJECT(u"reading from a value that's potentially affected by side effects"_s);
1700 const QString inputContentPointer = resolveValueTypeContentPointer(
1701 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1702 u"Cannot read property '%1' of %2"_s.arg(
1703 m_jsUnitGenerator->lookupName(index)));
1705 if (scope.contains(m_typeResolver->variantMapType())) {
1706 m_body += generateVariantMapGetLookup(
1707 u"(*static_cast<const QVariantMap *>("_s
1708 + inputContentPointer + u"))"_s, index);
1712 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1713 + u", "_s + inputContentPointer
1714 + u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
1716 const QString initialization = u"aotContext->initGetValueLookup("_s
1717 + indexString + u", "_s
1718 + metaObject(scope.containedType()) + u')';
1719 const QString preparation = getLookupPreparation(
1720 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1721 recordPropertyLookup(m_typeResolver->originalContainedType(scope),
1722 originalProperty(m_state.accumulatorOut()));
1723 generateLookup(lookup, initialization, preparation);
1727void QQmlJSCodeGenerator::generate_GetOptionalLookup(
int index,
int offset)
1731 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
1732 QString accumulatorVarIn = m_state.accumulatorVariableIn;
1734 const auto &annotation = m_annotations[currentInstructionOffset()];
1735 if (accumulatorIn.storedType()->isReferenceType()) {
1736 m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
1737 generateJumpCodeWithTypeConversions(offset);
1738 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
1739 m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
1740 "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
1741 generateJumpCodeWithTypeConversions(offset);
1742 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
1743 m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
1744 "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
1745 generateJumpCodeWithTypeConversions(offset);
1746 }
else if (annotation.changedRegisterIndex == Accumulator
1747 && annotation.changedRegister.variant() == QQmlJSRegisterContent::Enum) {
1749 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1750 m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
1751 generateJumpCodeWithTypeConversions(offset);
1752 }
else if (!m_typeResolver->canHoldUndefined(accumulatorIn.storage())) {
1758 generate_GetLookupHelper(index);
1761void QQmlJSCodeGenerator::generate_StoreProperty(
int nameIndex,
int baseReg)
1765 REJECT(u"StoreProperty"_s);
1773 Q_ASSERT(contentPointer.startsWith(u'&') || contentPointer[0].isLetterOrNumber());
1774 return contentPointer.startsWith(u'&') ? contentPointer.mid(1) : (u'*' + contentPointer);
1777void QQmlJSCodeGenerator::generate_SetLookup(
int index,
int baseReg)
1781 const QString indexString = QString::number(index);
1782 const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
1783 const QQmlJSRegisterContent property = m_state.readAccumulator();
1784 Q_ASSERT(property.isConversion());
1785 const QQmlJSRegisterContent original
1786 = m_typeResolver->original(property.conversionResultScope());
1787 const QQmlJSScope::ConstPtr originalScope = original.containedType();
1789 const QString &propertyName = m_jsUnitGenerator->lookupName(index);
1790 if (property.storedType().isNull()) {
1791 REJECT(u"SetLookup. Could not find property " + propertyName + u" on type "
1792 + originalScope->internalName());
1795 const QString object = registerVariable(baseReg);
1798 if (!m_state.accumulatorIn().contains(property.containedType())) {
1799 m_body += property.storedType()->augmentedInternalName() + u" converted = "_s
1800 + conversion(m_state.accumulatorIn(), property, consumedAccumulatorVariableIn())
1802 variableIn = contentPointer(property, u"converted"_s);
1804 variableIn = contentPointer(property, m_state.accumulatorVariableIn);
1807 switch (originalScope->accessSemantics()) {
1808 case QQmlJSScope::AccessSemantics::Reference: {
1809 const QString basePointer = resolveQObjectPointer(
1810 originalScope, registerType(baseReg), object,
1811 u"TypeError: Value is %1 and could not be converted to an object"_s);
1813 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1814 + u", "_s + basePointer + u", "_s + variableIn + u')';
1818 const QString initialization
1819 = (property.contains(m_typeResolver->varType())
1820 ? u"aotContext->initSetObjectLookupAsVariant("_s
1821 : u"aotContext->initSetObjectLookup("_s)
1822 + indexString + u", "_s + basePointer + u')';
1823 recordPropertyLookup(originalScope, originalScope->property(propertyName));
1824 generateLookup(lookup, initialization);
1827 case QQmlJSScope::AccessSemantics::Sequence: {
1828 if (propertyName != u"length"_s)
1829 REJECT(u"setting non-length property on a sequence type"_s);
1831 if (!originalScope->isListProperty())
1832 REJECT(u"resizing sequence types (because of missing write-back)"_s);
1835 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1836 m_body += u"const int end = "_s + derefContentPointer(variableIn) + u";\n"_s;
1837 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1838 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1839 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1840 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1844 case QQmlJSScope::AccessSemantics::Value: {
1845 const QQmlJSRegisterContent base = registerType(baseReg);
1846 if (base.isStoredIn(m_typeResolver->variantMapType())) {
1847 m_body += generateVariantMapSetLookup(
1848 registerVariable(baseReg), index, property.storedType(),
1849 derefContentPointer(variableIn));
1850 generateWriteBack(baseReg);
1853 const QString baseContentPointer = resolveValueTypeContentPointer(
1854 originalScope, base, object,
1855 u"TypeError: Value is %1 and could not be converted to an object"_s);
1857 if (original.contains(m_typeResolver->variantMapType())) {
1858 m_body += generateVariantMapSetLookup(
1859 u"(*static_cast<const QVariantMap *>("_s
1860 + baseContentPointer + u"))"_s, index, property.storedType(),
1861 derefContentPointer(variableIn));
1862 generateWriteBack(baseReg);
1866 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1867 + u", "_s + baseContentPointer
1868 + u", "_s + variableIn + u')';
1872 const QString initialization
1873 = (property.contains(m_typeResolver->varType())
1874 ? u"aotContext->initSetValueLookupAsVariant("_s
1875 : u"aotContext->initSetValueLookup("_s)
1876 + indexString + u", "_s + metaObject(originalScope) + u')';
1877 recordPropertyLookup(m_typeResolver->originalContainedType(base),
1878 originalScope->property(propertyName));
1879 generateLookup(lookup, initialization);
1880 generateWriteBack(baseReg);
1884 case QQmlJSScope::AccessSemantics::None:
1892void QQmlJSCodeGenerator::generate_LoadSuperProperty(
int property)
1898void QQmlJSCodeGenerator::generate_StoreSuperProperty(
int property)
1904void QQmlJSCodeGenerator::generate_Yield()
1909void QQmlJSCodeGenerator::generate_YieldStar()
1914void QQmlJSCodeGenerator::generate_Resume(
int)
1919QString QQmlJSCodeGenerator::initAndCall(
1920 int argc,
int argv,
const QString &callMethodTemplate,
const QString &initMethodTemplate,
1925 if (m_state.changedRegisterIndex() == InvalidRegister ||
1926 m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
1927 args = u"nullptr"_s;
1929 *outVar = u"callResult"_s;
1930 const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
1931 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1934 args = contentPointer(m_state.accumulatorOut(), *outVar);
1940 QString argumentPreparation;
1941 for (
int i = 0; i < argc; ++i) {
1942 if (isRegisterAffectedBySideEffects(argv + i))
1943 REJECT<QString>(u"calling method with argument affected by side effects"_s);
1944 const QQmlJSRegisterContent content = registerType(argv + i);
1945 const QQmlJSRegisterContent read = m_state.readRegister(argv + i);
1946 if (read.contains(content.containedType())) {
1947 args += u", "_s + contentPointer(read, registerVariable(argv + i));
1949 const QString var = u"arg"_s + QString::number(i);
1950 argumentPreparation +=
1951 u" "_s + read.storedType()->augmentedInternalName() + u' ' + var + u" = "_s
1952 + conversion(content, read, consumedRegisterVariable(argv + i)) + u";\n";
1953 args += u", "_s + contentPointer(read, var);
1959 if (m_state.isShadowable()) {
1960 initMethod = initMethodTemplate;
1962 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
1963 Q_ASSERT(!method.isConstructor());
1965 initMethod = initMethodTemplate.arg(
int(method.methodIndex()));
1968 return u"const auto doCall = [&]() {\n"_s
1969 + argumentPreparation
1970 + u" void *args[] = {" + args + u"};\n"_s
1971 + u" return aotContext->"_s + callMethodTemplate.arg(u"args"_s).arg(argc) + u";\n"
1973 + u"const auto doInit = [&]() {\n"_s
1974 + u" aotContext->"_s + initMethod + u";\n"
1978void QQmlJSCodeGenerator::generateMoveOutVarAfterCall(
const QString &outVar)
1980 if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
1983 m_body += m_state.accumulatorVariableOut + u" = "_s + u"std::move(" + outVar + u");\n";
1984 m_body += u"aotContext->setImplicitDestructible("_s
1985 + m_state.accumulatorVariableOut + u");\n"_s;
1988void QQmlJSCodeGenerator::generate_CallValue(
int name,
int argc,
int argv)
1996void QQmlJSCodeGenerator::generate_CallWithReceiver(
int name,
int thisObject,
int argc,
int argv)
1999 Q_UNUSED(thisObject)
2005void QQmlJSCodeGenerator::generate_CallProperty(
int nameIndex,
int baseReg,
int argc,
int argv)
2007 Q_UNUSED(nameIndex);
2011 REJECT(u"CallProperty"_s);
2014bool QQmlJSCodeGenerator::inlineStringMethod(
const QString &name,
int base,
int argc,
int argv)
2016 if (name != u"arg"_s || argc != 1)
2019 const auto arg = [&](
const QQmlJSScope::ConstPtr &type) {
2020 return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
2023 const auto ret = [&](
const QString &arg) {
2024 const QString expression = convertStored(
2025 registerType(base).storedType(), m_typeResolver->stringType(),
2026 consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
2028 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
2031 const QQmlJSRegisterContent input = m_state.readRegister(argv);
2032 m_body += m_state.accumulatorVariableOut + u" = "_s;
2034 if (m_typeResolver->isNumeric(input))
2035 m_body += ret(arg(input.containedType()));
2036 else if (input.contains(m_typeResolver->boolType()))
2037 m_body += ret(arg(m_typeResolver->boolType()));
2039 m_body += ret(arg(m_typeResolver->stringType()));
2044bool QQmlJSCodeGenerator::inlineTranslateMethod(
const QString &name,
int argc,
int argv)
2046 addInclude(u"QtCore/qcoreapplication.h"_s);
2048 const auto arg = [&](
int i,
const QQmlJSScope::ConstPtr &type) {
2050 return convertStored(registerType(argv + i).storedType(), type,
2051 consumedRegisterVariable(argv + i));
2054 const auto stringArg = [&](
int i) {
2056 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
2060 const auto intArg = [&](
int i) {
2061 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
2064 const auto stringRet = [&](
const QString &expression) {
2066 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
2069 const auto capture = [&]() {
2070 m_body += u"aotContext->captureTranslation();\n"_s;
2073 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
2075 m_body += m_state.accumulatorVariableOut + u" = "_s
2076 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
2080 if (name == u"QT_TRANSLATE_NOOP"_s) {
2082 m_body += m_state.accumulatorVariableOut + u" = "_s
2083 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
2087 if (name == u"qsTrId"_s) {
2092 m_body += m_state.accumulatorVariableOut + u" = "_s
2093 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
2094 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
2098 if (name == u"qsTr"_s) {
2100 m_body += m_state.accumulatorVariableOut + u" = "_s
2101 + stringRet(u"QCoreApplication::translate("_s
2102 + u"aotContext->translationContext().toUtf8().constData(), "_s
2103 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2104 + intArg(2) + u")"_s) + u";\n"_s;
2108 if (name == u"qsTranslate"_s) {
2110 m_body += m_state.accumulatorVariableOut + u" = "_s
2111 + stringRet(u"QCoreApplication::translate("_s
2112 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2113 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
2122 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"max() expects at least two arguments.");
2124 QString expression =
2125 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2126 for (
int i = 2; i < argc; i++) {
2128 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
2129 .arg(
"arg"_L1 + QString::number(i + 1));
2131 expression +=
"return tmpMax;\n}()"_L1;
2138 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"min() expects at least two arguments.");
2140 QString expression =
2141 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2142 for (
int i = 2; i < argc; i++) {
2144 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
2145 .arg(
"arg"_L1 + QString::number(i + 1));
2147 expression +=
"return tmpMin;\n}()"_L1;
2152bool QQmlJSCodeGenerator::inlineMathMethod(
const QString &name,
int argc,
int argv)
2154 addInclude(u"cmath"_s);
2155 addInclude(u"limits"_s);
2156 addInclude(u"QtCore/qalgorithms.h"_s);
2157 addInclude(u"QtCore/qrandom.h"_s);
2158 addInclude(u"QtQml/qjsprimitivevalue.h"_s);
2162 if (m_state.changedRegisterIndex() != Accumulator)
2166 for (
int i = 0; i < argc; ++i) {
2167 m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
2168 registerType(argv + i).storedType(),
2169 m_typeResolver->realType(), consumedRegisterVariable(argv + i))
2173 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
2174 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
2175 m_body += m_state.accumulatorVariableOut + u" = "_s;
2179 if (name == u"abs" && argc == 1) {
2180 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
2181 }
else if (name == u"acos"_s && argc == 1) {
2182 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
2183 }
else if (name == u"acosh"_s && argc == 1) {
2184 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
2185 }
else if (name == u"asin"_s && argc == 1) {
2186 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
2187 }
else if (name == u"asinh"_s && argc == 1) {
2188 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
2189 }
else if (name == u"atan"_s && argc == 1) {
2190 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
2191 }
else if (name == u"atanh"_s && argc == 1) {
2192 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
2193 }
else if (name == u"atan2"_s) {
2196 }
else if (name == u"cbrt"_s && argc == 1) {
2197 expression = u"std::cbrt(arg1)"_s;
2198 }
else if (name == u"ceil"_s && argc == 1) {
2199 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
2200 }
else if (name == u"clz32"_s && argc == 1) {
2201 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
2202 }
else if (name == u"cos"_s && argc == 1) {
2203 expression = u"std::cos(arg1)"_s;
2204 }
else if (name == u"cosh"_s && argc == 1) {
2205 expression = u"std::cosh(arg1)"_s;
2206 }
else if (name == u"exp"_s && argc == 1) {
2207 expression = u"std::isinf(arg1) "
2208 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
2209 ": std::exp(arg1)"_s.arg(inf);
2210 }
else if (name == u"expm1"_s) {
2213 }
else if (name == u"floor"_s && argc == 1) {
2214 expression = u"std::floor(arg1)"_s;
2215 }
else if (name == u"fround"_s && argc == 1) {
2216 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
2218 ": double(float(arg1))"_s;
2219 }
else if (name == u"hypot"_s) {
2222 }
else if (name == u"imul"_s && argc == 2) {
2223 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
2224 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
2225 }
else if (name == u"log"_s && argc == 1) {
2226 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
2227 }
else if (name == u"log10"_s && argc == 1) {
2228 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
2229 }
else if (name == u"log1p"_s && argc == 1) {
2230 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
2231 }
else if (name == u"log2"_s && argc == 1) {
2232 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
2233 }
else if (name == u"max"_s && argc >= 2) {
2234 expression = maxExpression(argc);
2235 }
else if (name == u"min"_s && argc >= 2) {
2236 expression = minExpression(argc);
2237 }
else if (name == u"pow"_s) {
2238 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
2239 }
else if (name == u"random"_s && argc == 0) {
2240 expression = u"QRandomGenerator::global()->generateDouble()"_s;
2241 }
else if (name == u"round"_s && argc == 1) {
2242 expression = u"std::isfinite(arg1) "
2243 "? ((arg1 < 0.5 && arg1 >= -0.5) "
2244 "? std::copysign(0.0, arg1) "
2245 ": std::floor(arg1 + 0.5)) "
2247 }
else if (name == u"sign"_s && argc == 1) {
2248 expression = u"std::isnan(arg1) "
2252 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
2253 }
else if (name == u"sin"_s && argc == 1) {
2254 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
2255 }
else if (name == u"sinh"_s && argc == 1) {
2256 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
2257 }
else if (name == u"sqrt"_s && argc == 1) {
2258 expression = u"std::sqrt(arg1)"_s;
2259 }
else if (name == u"tan"_s && argc == 1) {
2260 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
2261 }
else if (name == u"tanh"_s && argc == 1) {
2262 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
2263 }
else if (name == u"trunc"_s && argc == 1) {
2264 expression = u"std::trunc(arg1)"_s;
2269 m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
2278 if (method == u"log" || method == u"debug")
2279 return u"QtDebugMsg"_s;
2280 if (method == u"info")
2281 return u"QtInfoMsg"_s;
2282 if (method == u"warn")
2283 return u"QtWarningMsg"_s;
2284 if (method == u"error")
2285 return u"QtCriticalMsg"_s;
2289bool QQmlJSCodeGenerator::inlineConsoleMethod(
const QString &name,
int argc,
int argv)
2291 const QString type = messageTypeForMethod(name);
2295 addInclude(u"QtCore/qloggingcategory.h"_s);
2298 m_body += u" bool firstArgIsCategory = false;\n";
2299 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
2304 const bool firstArgIsReference = argc > 0
2305 && firstArg.containedType()->isReferenceType();
2307 if (firstArgIsReference) {
2308 m_body += u" QObject *firstArg = ";
2309 m_body += convertStored(
2310 firstArg.storedType(),
2311 m_typeResolver->genericType(firstArg.storedType()),
2312 registerVariable(argv));
2316 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
2317 m_body += firstArgIsReference ? u"firstArg"_sv : u"nullptr"_sv;
2318 m_body += u", &firstArgIsCategory);\n";
2319 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
2321 m_body += u" const QString message = ";
2323 const auto stringConversion = [&](
int i) -> QString {
2324 const QQmlJSScope::ConstPtr read = m_state.readRegister(argv + i).storedType();
2325 const QQmlJSScope::ConstPtr actual = registerType(argv + i).storedType();
2326 if (read == m_typeResolver->stringType()) {
2327 return convertStored(actual, read, consumedRegisterVariable(argv + i));
2328 }
else if (actual->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2329 addInclude(u"QtQml/qjslist.h"_s);
2330 return u"(u'[' + QJSList(&"_s + registerVariable(argv + i)
2331 + u", aotContext->engine).toString() + u']')"_s;
2333 REJECT<QString>(u"converting arguments for console method to string"_s);
2338 if (firstArgIsReference) {
2339 const QString firstArgStringConversion = convertStored(
2340 registerType(argv).storedType(),
2341 m_typeResolver->stringType(), registerVariable(argv));
2342 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
2344 m_body += u".append(QLatin1Char(' ')))).append(";
2348 m_body += stringConversion(0);
2350 m_body += u".append(QLatin1Char(' ')).append(";
2353 for (
int i = 1; i < argc; ++i) {
2355 m_body += u".append(QLatin1Char(' ')).append("_s;
2356 m_body += stringConversion(i) + u')';
2359 m_body += u"QString()";
2362 generateSetInstructionPointer();
2363 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
2369bool QQmlJSCodeGenerator::inlineArrayMethod(
const QString &name,
int base,
int argc,
int argv)
2371 const auto intType = m_typeResolver->int32Type();
2372 const auto elementType = registerType(base).storedType()->elementType();
2373 const auto boolType = m_typeResolver->boolType();
2374 const auto stringType = m_typeResolver->stringType();
2375 const auto baseType = registerType(base);
2377 const QString baseVar = registerVariable(base);
2378 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
2381 addInclude(u"QtQml/qjslist.h"_s);
2383 if (name == u"includes" && argc > 0 && argc < 3) {
2384 QString call = qjsListMethod
2385 + convertStored(registerType(argv).storedType(), elementType,
2386 consumedRegisterVariable(argv));
2388 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2389 consumedRegisterVariable(argv + 1));
2393 m_body += m_state.accumulatorVariableOut + u" = "_s
2394 + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
2398 if (name == u"toString" || (name == u"join" && argc < 2)) {
2399 QString call = qjsListMethod;
2401 call += convertStored(registerType(argv).storedType(), stringType,
2402 consumedRegisterVariable(argv));
2406 m_body += m_state.accumulatorVariableOut + u" = "_s
2407 + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
2411 if (name == u"slice" && argc < 3) {
2412 QString call = qjsListMethod;
2413 for (
int i = 0; i < argc; ++i) {
2416 call += convertStored(registerType(argv + i).storedType(), intType,
2417 consumedRegisterVariable(argv + i));
2421 m_body += m_state.accumulatorVariableOut + u" = "_s;
2422 if (baseType.storedType()->isListProperty())
2423 m_body += conversion(m_typeResolver->qObjectListType(), m_state.accumulatorOut(), call);
2425 m_body += conversion(baseType, m_state.accumulatorOut(), call);
2431 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2432 QString call = qjsListMethod
2433 + convertStored(registerType(argv).storedType(), elementType,
2434 consumedRegisterVariable(argv));
2436 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2437 consumedRegisterVariable(argv + 1));
2441 m_body += m_state.accumulatorVariableOut + u" = "_s
2442 + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
2449void QQmlJSCodeGenerator::generate_CallPropertyLookup(
int index,
int base,
int argc,
int argv)
2453 const QQmlJSRegisterContent scopeContent = m_state.accumulatorOut().scope();
2454 const QQmlJSScope::ConstPtr scope = scopeContent.containedType();
2456 AccumulatorConverter registers(
this);
2458 const QQmlJSRegisterContent baseType = registerType(base);
2459 const QString name = m_jsUnitGenerator->lookupName(index);
2461 if (scope == m_typeResolver->mathObject()) {
2462 if (inlineMathMethod(name, argc, argv))
2464 }
else if (scope == m_typeResolver->consoleObject()) {
2465 if (inlineConsoleMethod(name, argc, argv))
2467 }
else if (scope == m_typeResolver->stringType()) {
2468 if (inlineStringMethod(name, base, argc, argv))
2470 }
else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2471 if (inlineArrayMethod(name, base, argc, argv))
2475 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2476 REJECT(u"call to untyped JavaScript function"_s);
2481 if (scope->isReferenceType()) {
2482 const QString inputPointer = resolveQObjectPointer(
2483 scope, baseType, registerVariable(base),
2484 u"Cannot call method '%1' of %2"_s.arg(name));
2486 const QString initMethodTemplate = m_state.isShadowable()
2487 ? u"initCallObjectPropertyLookupAsVariant(%1, %2)"_s
2488 : u"initCallObjectPropertyLookup(%1, %2, %3)"_s;
2490 m_body += initAndCall(
2492 u"callObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2493 initMethodTemplate.arg(index).arg(inputPointer), &outVar);
2495 const QQmlJSScope::ConstPtr originalScope
2496 = m_typeResolver->original(scopeContent).containedType();
2497 const QString inputPointer = resolveValueTypeContentPointer(
2498 originalScope, baseType, registerVariable(base),
2499 u"Cannot call method '%1' of %2"_s.arg(name));
2501 m_body += initAndCall(
2503 u"callValueLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2504 u"initCallValueLookup(%1, %2, %3)"_s
2505 .arg(index).arg(metaObject(originalScope)),
2509 const QString lookup = u"doCall()"_s;
2510 const QString initialization = u"doInit()"_s;
2511 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2512 recordMethodLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
2513 originalMethod(m_state.accumulatorOut()));
2514 generateLookup(lookup, initialization, preparation);
2515 generateMoveOutVarAfterCall(outVar);
2519 if (scope->isReferenceType())
2522 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
2523 if (!method.isConst())
2524 generateWriteBack(base);
2527void QQmlJSCodeGenerator::generate_CallName(
int name,
int argc,
int argv)
2535void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(
int argc,
int argv)
2542void QQmlJSCodeGenerator::generate_CallGlobalLookup(
int index,
int argc,
int argv)
2547 REJECT(u"CallGlobalLookup"_s);
2550void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(
int index,
int argc,
int argv)
2554 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
2555 const QString name = m_jsUnitGenerator->stringForIndex(
2556 m_jsUnitGenerator->lookupNameIndex(index));
2557 if (inlineTranslateMethod(name, argc, argv))
2561 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2562 REJECT(u"call to untyped JavaScript function"_s);
2564 AccumulatorConverter registers(
this);
2568 m_body += initAndCall(
2569 argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
2570 u"initCallQmlContextPropertyLookup(%1, %2)"_s.arg(index), &outVar);
2572 const QString lookup = u"doCall()"_s;
2573 const QString initialization = u"doInit()"_s;
2574 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2575 recordMethodLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
2576 originalMethod(m_state.accumulatorOut()));
2577 generateLookup(lookup, initialization, preparation);
2578 generateMoveOutVarAfterCall(outVar);
2583void QQmlJSCodeGenerator::generate_CallWithSpread(
int func,
int thisObject,
int argc,
int argv)
2586 Q_UNUSED(thisObject)
2592void QQmlJSCodeGenerator::generate_TailCall(
int func,
int thisObject,
int argc,
int argv)
2595 Q_UNUSED(thisObject)
2601void QQmlJSCodeGenerator::generate_Construct(
int func,
int argc,
int argv)
2606 const auto originalResult = originalType(m_state.accumulatorOut());
2608 if (originalResult.contains(m_typeResolver->dateTimeType())) {
2609 m_body += m_state.accumulatorVariableOut + u" = ";
2611 m_body += conversion(
2612 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2613 u"QDateTime::currentDateTime()"_s) + u";\n";
2617 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->dateTimeType())) {
2618 m_body += conversion(
2619 registerType(argv), m_state.readRegister(argv), registerVariable(argv))
2625 constexpr int maxArgc = 7;
2626 for (
int i = 0; i < std::min(argc, maxArgc); ++i) {
2629 ctorArgs += conversion(
2630 registerType(argv + i), m_state.readRegister(argv + i),
2631 registerVariable(argv + i));
2633 m_body += conversion(
2634 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2635 u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
2639 if (originalResult.contains(m_typeResolver->variantListType())) {
2642 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->realType())) {
2643 addInclude(u"QtQml/qjslist.h"_s);
2645 const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
2646 + u"QLatin1String(\"Invalid array length\"));\n"_s;
2648 const QString indexName = registerVariable(argv);
2649 const auto indexType = registerType(argv).containedType();
2650 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
2651 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
2653 generateReturnError();
2655 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
2656 m_body += u"if ("_s + indexName + u" < 0) {\n"_s
2658 generateReturnError();
2662 m_body += m_state.accumulatorVariableOut + u" = "_s
2663 + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
2664 m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
2665 + u", aotContext->engine).resize("_s
2667 registerType(argv).storedType(), m_typeResolver->sizeType(),
2668 consumedRegisterVariable(argv))
2670 }
else if (!m_logger->currentFunctionHasCompileError()) {
2671 generateArrayInitializer(argc, argv);
2676 const QQmlJSScope::ConstPtr originalContained = originalResult.containedType();
2677 if (originalContained->isValueType() && originalResult.isMethodCall()) {
2678 const QQmlJSMetaMethod ctor = originalResult.methodCall();
2679 if (ctor.isJavaScriptFunction())
2680 REJECT(u"calling JavaScript constructor "_s + ctor.methodName());
2682 QList<QQmlJSRegisterContent> argumentTypes;
2683 QStringList arguments;
2684 for (
int i = 0; i < argc; ++i) {
2685 argumentTypes.append(registerType(argv + i));
2686 arguments.append(consumedRegisterVariable(argv + i));
2689 const QQmlJSScope::ConstPtr extension = originalContained->extensionType().scope;
2690 const QString result = generateCallConstructor(
2691 ctor, argumentTypes, arguments, metaType(originalContained),
2692 metaObject(extension ? extension : originalContained));
2694 m_body += m_state.accumulatorVariableOut + u" = "_s
2695 + conversion(m_pool->storedIn(originalResult, m_typeResolver->varType()),
2696 m_state.accumulatorOut(), result)
2706void QQmlJSCodeGenerator::generate_ConstructWithSpread(
int func,
int argc,
int argv)
2714void QQmlJSCodeGenerator::generate_SetUnwindHandler(
int offset)
2717 REJECT(u"SetUnwindHandler"_s);
2720void QQmlJSCodeGenerator::generate_UnwindDispatch()
2722 REJECT(u"UnwindDispatch"_s);
2725void QQmlJSCodeGenerator::generate_UnwindToLabel(
int level,
int offset)
2732void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(
int name)
2740void QQmlJSCodeGenerator::generate_ThrowException()
2744 generateSetInstructionPointer();
2745 m_body += u"aotContext->engine->throwError("_s + conversion(
2746 m_state.accumulatorIn(),
2747 m_typeResolver->jsValueType(),
2748 m_state.accumulatorVariableIn) + u");\n"_s;
2749 generateReturnError();
2750 m_skipUntilNextLabel =
true;
2754void QQmlJSCodeGenerator::generate_GetException()
2759void QQmlJSCodeGenerator::generate_SetException()
2764void QQmlJSCodeGenerator::generate_CreateCallContext()
2771void QQmlJSCodeGenerator::generate_PushCatchContext(
int index,
int nameIndex)
2775 REJECT(u"PushCatchContext"_s);
2778void QQmlJSCodeGenerator::generate_PushWithContext()
2783void QQmlJSCodeGenerator::generate_PushBlockContext(
int index)
2789void QQmlJSCodeGenerator::generate_CloneBlockContext()
2794void QQmlJSCodeGenerator::generate_PushScriptContext(
int index)
2800void QQmlJSCodeGenerator::generate_PopScriptContext()
2805void QQmlJSCodeGenerator::generate_PopContext()
2810 m_body += u"{}\n}\n"_s;
2813void QQmlJSCodeGenerator::generate_GetIterator(
int iterator)
2817 addInclude(u"QtQml/qjslist.h"_s);
2818 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2819 if (!listType.isList())
2820 REJECT(u"iterator on non-list type"_s);
2822 const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
2823 if (!iteratorType.isProperty())
2824 REJECT(u"using non-iterator as iterator"_s);
2826 const QString identifier = QString::number(iteratorType.baseLookupIndex());
2827 QString baseName = m_state.accumulatorVariableOut.mid(2);
2828 const QString iteratorName = baseName + u"Iterator" + identifier;
2829 const QString listName = baseName + u"List" + identifier;
2831 m_body += u"QJSListFor"_s
2832 + (iterator ==
int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
2833 + u"Iterator "_s + iteratorName + u";\n";
2834 m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
2836 m_body += m_state.accumulatorVariableOut + u"->init(";
2837 if (iterator ==
int(QQmlJS::AST::ForEachType::In)) {
2838 if (!iteratorType.isStoredIn(m_typeResolver->forInIteratorPtr()))
2839 REJECT(u"using non-iterator as iterator"_s);
2840 m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
2844 if (iterator ==
int(QQmlJS::AST::ForEachType::Of)) {
2845 if (!iteratorType.isStoredIn(m_typeResolver->forOfIteratorPtr()))
2846 REJECT(u"using non-iterator as iterator"_s);
2847 m_body += u"const auto &"
2848 + listName + u" = " + consumedAccumulatorVariableIn();
2852void QQmlJSCodeGenerator::generate_IteratorNext(
int value,
int offset)
2856 Q_ASSERT(value == m_state.changedRegisterIndex());
2857 const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
2858 if (!iteratorContent.isProperty())
2859 REJECT(u"using non-iterator as iterator"_s);
2861 const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
2862 const QString listName = m_state.accumulatorVariableIn
2863 + u"List" + QString::number(iteratorContent.baseLookupIndex());
2865 if (iteratorType == m_typeResolver->forOfIteratorPtr())
2866 qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
2867 else if (iteratorType != m_typeResolver->forInIteratorPtr())
2868 REJECT(u"using non-iterator as iterator"_s);
2870 m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
2873 QQmlJSRegisterContent iteratorValue = m_typeResolver->extractNonVoidFromOptionalType(
2874 m_typeResolver->original(m_state.changedRegister()));
2875 iteratorValue = m_pool->storedIn(iteratorValue, iteratorValue.containedType());
2877 m_body += changedRegisterVariable() + u" = "
2879 iteratorValue, m_state.changedRegister(),
2880 m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
2882 m_body += u"} else {\n ";
2883 m_body += changedRegisterVariable() + u" = "
2884 + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
2886 generateJumpCodeWithTypeConversions(offset);
2890void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(
int iterator,
int object,
int offset)
2898void QQmlJSCodeGenerator::generate_IteratorClose()
2903void QQmlJSCodeGenerator::generate_DestructureRestElement()
2908void QQmlJSCodeGenerator::generate_DeleteProperty(
int base,
int index)
2915void QQmlJSCodeGenerator::generate_DeleteName(
int name)
2921void QQmlJSCodeGenerator::generate_TypeofName(
int name)
2927void QQmlJSCodeGenerator::generate_TypeofValue()
2929 REJECT(u"TypeofValue"_s);
2932void QQmlJSCodeGenerator::generate_DeclareVar(
int varName,
int isDeletable)
2935 Q_UNUSED(isDeletable)
2939void QQmlJSCodeGenerator::generate_DefineArray(
int argc,
int args)
2944 if (!m_logger->currentFunctionHasCompileError())
2945 generateArrayInitializer(argc, args);
2948void QQmlJSCodeGenerator::generate_DefineObjectLiteral(
int internalClassId,
int argc,
int args)
2952 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
2953 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value)
2954 REJECT(u"storing an object literal in a non-value type"_s);
2956 const QQmlJSScope::ConstPtr contained = m_state.accumulatorOut().containedType();
2958 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2965 if (argc > classSize)
2966 REJECT(u"non-literal keys of object literals"_s);
2968 Q_ASSERT(argc == classSize);
2970 const auto createVariantMap = [&]() {
2972 result += u"QVariantMap {\n";
2973 const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
2974 for (
int i = 0; i < classSize; ++i) {
2976 + QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
2978 const int currentArg = args + i;
2979 const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
2980 const QString consumedArg = consumedRegisterVariable(currentArg);
2981 result += convertStored(argType, propType, consumedArg) + u" },\n";
2989 if (contained == m_typeResolver->varType() || contained == m_typeResolver->variantMapType()) {
2990 m_body += m_state.accumulatorVariableOut + u" = "_s + createVariantMap() + u";\n"_s;
2994 if (contained == m_typeResolver->jsValueType()) {
2995 m_body += m_state.accumulatorVariableOut + u" = aotContext->engine->toScriptValue("_s
2996 + createVariantMap() + u");\n"_s;
3000 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
3001 const bool isVariantOrPrimitive = (stored == m_typeResolver->varType())
3002 || (stored == m_typeResolver->jsPrimitiveType());
3004 if (m_state.accumulatorOut().contains(stored)) {
3006 }
else if (isVariantOrPrimitive) {
3007 m_body += u'(' + metaType(m_state.accumulatorOut().containedType()) + u')';
3009 REJECT(u"storing an object literal in an unsupported container %1"_s
3010 .arg(stored->internalName()));
3017 bool isExtension =
false;
3018 if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension))
3019 REJECT(u"storing an object literal in a non-structured value type"_s);
3021 const QQmlJSScope::ConstPtr accessor = isExtension
3022 ? contained->extensionType().scope
3026 m_body += u" const QMetaObject *meta = ";
3027 if (!isExtension && isVariantOrPrimitive)
3028 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
3030 m_body += metaObject(accessor);
3033 for (
int i = 0; i < classSize; ++i) {
3035 const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
3036 const int currentArg = args + i;
3037 const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
3038 const QQmlJSRegisterContent argType = registerType(currentArg);
3039 const QQmlJSMetaProperty property = contained->property(propName);
3040 const QString consumedArg = consumedRegisterVariable(currentArg);
3041 QString argument = conversion(argType, propType, consumedArg);
3043 if (argument == consumedArg) {
3044 argument = registerVariable(currentArg);
3046 m_body += u" "_s + propType.storedType()->augmentedInternalName()
3047 + u" arg = "_s + argument + u";\n";
3048 argument = u"arg"_s;
3051 int index = property.index();
3055 const QString indexString = QString::number(index);
3056 m_body += u" void *argv[] = { %1, nullptr };\n"_s
3057 .arg(contentPointer(propType, argument));
3058 m_body += u" meta->property("_s + indexString;
3059 m_body += u").enclosingMetaObject()->d.static_metacall(reinterpret_cast<QObject *>(";
3060 m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
3061 m_body += u"), QMetaObject::WriteProperty, " + indexString + u", argv);\n";
3069void QQmlJSCodeGenerator::generate_CreateClass(
int classIndex,
int heritage,
int computedNames)
3071 Q_UNUSED(classIndex)
3073 Q_UNUSED(computedNames)
3077void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
3082void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
3087void QQmlJSCodeGenerator::generate_CreateRestParameter(
int argIndex)
3093void QQmlJSCodeGenerator::generate_ConvertThisToObject()
3097 m_body += changedRegisterVariable() + u" = "_s
3098 + conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
3099 u"aotContext->thisObject()"_s)
3103void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
3108void QQmlJSCodeGenerator::generate_ToObject()
3113void QQmlJSCodeGenerator::generate_Jump(
int offset)
3117 generateJumpCodeWithTypeConversions(offset);
3118 m_skipUntilNextLabel =
true;
3122void QQmlJSCodeGenerator::generate_JumpTrue(
int offset)
3126 m_body += u"if ("_s;
3127 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3128 m_state.accumulatorVariableIn);
3130 generateJumpCodeWithTypeConversions(offset);
3133void QQmlJSCodeGenerator::generate_JumpFalse(
int offset)
3137 m_body += u"if (!"_s;
3138 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3139 m_state.accumulatorVariableIn);
3141 generateJumpCodeWithTypeConversions(offset);
3144void QQmlJSCodeGenerator::generate_JumpNoException(
int offset)
3148 m_body += u"if (!context->engine->hasException()) "_s;
3149 generateJumpCodeWithTypeConversions(offset);
3152void QQmlJSCodeGenerator::generate_JumpNotUndefined(
int offset)
3158void QQmlJSCodeGenerator::generate_CheckException()
3162 generateExceptionCheck();
3165void QQmlJSCodeGenerator::generate_CmpEqNull()
3168 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
false);
3171void QQmlJSCodeGenerator::generate_CmpNeNull()
3174 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
true);
3177QString QQmlJSCodeGenerator::getLookupPreparation(
3178 QQmlJSRegisterContent content,
const QString &var,
int lookup)
3180 if (content.contains(content.storedType()))
3183 if (content.storedType()->isOpaqueType()) {
3186 REJECT<QString>(
"Can't prepare lookup involving non-registered types"_L1);
3189 if (content.isStoredIn(m_typeResolver->varType())) {
3190 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
3191 + QString::number(lookup) + u"))"_s;
3194 if (content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3195 return var + u" = QJSPrimitiveValue(aotContext->lookupResultMetaType("_s
3196 + QString::number(lookup) + u"))"_s;
3203QString QQmlJSCodeGenerator::contentPointer(QQmlJSRegisterContent content,
const QString &var)
3205 const QQmlJSScope::ConstPtr stored = content.storedType();
3206 if (content.contains(stored))
3209 if (content.isStoredIn(m_typeResolver->varType())
3210 || content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3211 return var + u".data()"_s;
3214 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3217 if (m_typeResolver->isNumeric(content.storedType())
3218 && content.containedType()->scopeType() == QQmlSA::ScopeType::EnumScope) {
3222 if (stored->isListProperty() && content.containedType()->isListProperty())
3226 u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
3229QString QQmlJSCodeGenerator::contentType(QQmlJSRegisterContent content,
const QString &var)
3231 const QQmlJSScope::ConstPtr stored = content.storedType();
3232 const QQmlJSScope::ConstPtr contained = content.containedType();
3233 if (contained == stored)
3234 return metaTypeFromType(stored);
3236 if (stored == m_typeResolver->varType() || stored == m_typeResolver->jsPrimitiveType())
3237 return var + u".metaType()"_s;
3239 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3240 return metaType(contained);
3242 const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
3243 if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
3244 return metaTypeFromType(nonComposite->baseType());
3246 if (stored->isListProperty() && contained->isListProperty())
3247 return metaType(contained);
3250 u"content type of unsupported wrapper type "_s + content.descriptiveName());
3253void QQmlJSCodeGenerator::generate_CmpEqInt(
int lhsConst)
3257 generateEqualityOperation(
3258 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
false);
3261void QQmlJSCodeGenerator::generate_CmpNeInt(
int lhsConst)
3265 generateEqualityOperation(
3266 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
true);
3269void QQmlJSCodeGenerator::generate_CmpEq(
int lhs)
3272 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
false);
3275void QQmlJSCodeGenerator::generate_CmpNe(
int lhs)
3278 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
true);
3281void QQmlJSCodeGenerator::generate_CmpGt(
int lhs)
3284 generateCompareOperation(lhs, u">"_s);
3287void QQmlJSCodeGenerator::generate_CmpGe(
int lhs)
3290 generateCompareOperation(lhs, u">="_s);
3293void QQmlJSCodeGenerator::generate_CmpLt(
int lhs)
3296 generateCompareOperation(lhs, u"<"_s);
3299void QQmlJSCodeGenerator::generate_CmpLe(
int lhs)
3302 generateCompareOperation(lhs, u"<="_s);
3305void QQmlJSCodeGenerator::generate_CmpStrictEqual(
int lhs)
3308 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
false);
3311void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(
int lhs)
3314 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
true);
3317void QQmlJSCodeGenerator::generate_CmpIn(
int lhs)
3323void QQmlJSCodeGenerator::generate_CmpInstanceOf(
int lhs)
3329void QQmlJSCodeGenerator::generate_As(
int lhs)
3333 const QString input = registerVariable(lhs);
3334 const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
3335 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
3339 const QQmlJSRegisterContent originalContent = originalType(outputContent);
3340 QQmlJSScope::ConstPtr target;
3341 if (originalContent.containedType()->isReferenceType())
3342 target = originalContent.containedType();
3343 else if (originalContent.isConversion())
3344 target = m_typeResolver->extractNonVoidFromOptionalType(originalContent).containedType();
3345 else if (originalContent.variant() == QQmlJSRegisterContent::Cast)
3346 target = originalContent.containedType();
3349 REJECT(u"type assertion to unknown type"_s);
3351 const bool isTrivial = m_typeResolver->inherits(
3352 m_typeResolver->originalContainedType(inputContent), target);
3354 m_body += m_state.accumulatorVariableOut + u" = "_s;
3356 if (!isTrivial && target->isReferenceType()) {
3357 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
3358 const QString inputConversion = inputContent.storedType()->isReferenceType()
3360 : convertStored(inputContent.storedType(), genericContained, input);
3362 if (target->isComposite()
3363 && m_state.accumulatorIn().isStoredIn(m_typeResolver->metaObjectType())) {
3364 m_body += conversion(
3365 genericContained, outputContent,
3366 m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
3368 m_body += conversion(
3369 genericContained, outputContent,
3370 u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
3376 if (inputContent.isStoredIn(m_typeResolver->varType())
3377 || inputContent.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3379 const auto source = m_typeResolver->extractNonVoidFromOptionalType(
3380 originalType(inputContent)).containedType();
3382 if (source && source == target) {
3383 m_body += input + u".metaType() == "_s + metaType(target)
3384 + u" ? " + conversion(inputContent, outputContent, input)
3385 + u" : " + conversion(
3386 literalType(m_typeResolver->voidType()), outputContent, QString());
3394 m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
3398 REJECT(u"non-trivial value type assertion"_s);
3401void QQmlJSCodeGenerator::generate_UNot()
3404 generateUnaryOperation(u"!"_s);
3407void QQmlJSCodeGenerator::generate_UPlus()
3410 generateUnaryOperation(u"+"_s);
3413void QQmlJSCodeGenerator::generate_UMinus()
3416 generateUnaryOperation(u"-"_s);
3419void QQmlJSCodeGenerator::generate_UCompl()
3422 generateUnaryOperation(u"~"_s);
3425void QQmlJSCodeGenerator::generate_Increment()
3428 generateInPlaceOperation(u"++"_s);
3431void QQmlJSCodeGenerator::generate_Decrement()
3434 generateInPlaceOperation(u"--"_s);
3437void QQmlJSCodeGenerator::generate_Add(
int lhs)
3440 generateArithmeticOperation(lhs, u"+"_s);
3443void QQmlJSCodeGenerator::generate_BitAnd(
int lhs)
3446 generateArithmeticOperation(lhs, u"&"_s);
3449void QQmlJSCodeGenerator::generate_BitOr(
int lhs)
3452 generateArithmeticOperation(lhs, u"|"_s);
3455void QQmlJSCodeGenerator::generate_BitXor(
int lhs)
3458 generateArithmeticOperation(lhs, u"^"_s);
3461void QQmlJSCodeGenerator::generate_UShr(
int lhs)
3464 generateShiftOperation(lhs, u">>"_s);
3467void QQmlJSCodeGenerator::generate_Shr(
int lhs)
3470 generateShiftOperation(lhs, u">>"_s);
3473void QQmlJSCodeGenerator::generate_Shl(
int lhs)
3476 generateShiftOperation(lhs, u"<<"_s);
3479void QQmlJSCodeGenerator::generate_BitAndConst(
int rhs)
3482 generateArithmeticConstOperation(rhs, u"&"_s);
3485void QQmlJSCodeGenerator::generate_BitOrConst(
int rhs)
3488 generateArithmeticConstOperation(rhs, u"|"_s);
3491void QQmlJSCodeGenerator::generate_BitXorConst(
int rhs)
3494 generateArithmeticConstOperation(rhs, u"^"_s);
3497void QQmlJSCodeGenerator::generate_UShrConst(
int rhs)
3500 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3503void QQmlJSCodeGenerator::generate_ShrConst(
int rhs)
3506 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3509void QQmlJSCodeGenerator::generate_ShlConst(
int rhs)
3512 generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
3515void QQmlJSCodeGenerator::generate_Exp(
int lhs)
3519 const QString lhsString = conversion(
3520 registerType(lhs), m_state.readRegister(lhs), consumedRegisterVariable(lhs));
3521 const QString rhsString = conversion(
3522 m_state.accumulatorIn(), m_state.readAccumulator(),
3523 consumedAccumulatorVariableIn());
3525 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsString.isEmpty());
3526 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsString.isEmpty());
3528 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3529 m_body += m_state.accumulatorVariableOut + u" = "_s;
3530 m_body += conversion(
3531 originalOut, m_state.accumulatorOut(),
3532 u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
3536void QQmlJSCodeGenerator::generate_Mul(
int lhs)
3539 generateArithmeticOperation(lhs, u"*"_s);
3542void QQmlJSCodeGenerator::generate_Div(
int lhs)
3545 GeneratePragmaWarningBlock warningBlock(
this);
3546 warningBlock.silenceDivideByZero();
3547 generateArithmeticOperation(lhs, u"/"_s);
3550void QQmlJSCodeGenerator::generate_Mod(
int lhs)
3554 const auto lhsVar = convertStored(
3555 registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
3556 consumedRegisterVariable(lhs));
3557 const auto rhsVar = convertStored(
3558 m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
3559 consumedAccumulatorVariableIn());
3560 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsVar.isEmpty());
3561 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsVar.isEmpty());
3563 m_body += m_state.accumulatorVariableOut;
3565 m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
3566 u'(' + lhsVar + u" % "_s + rhsVar + u')');
3570void QQmlJSCodeGenerator::generate_Sub(
int lhs)
3573 generateArithmeticOperation(lhs, u"-"_s);
3576void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(
int firstReg,
int count)
3583void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
3588void QQmlJSCodeGenerator::generate_GetTemplateObject(
int index)
3594QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
3595 QV4::Moth::Instr::Type type)
3597 m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
3598 const auto accumulatorIn = m_state.registers.find(Accumulator);
3599 if (accumulatorIn != m_state.registers.end()
3600 && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
3601 QQmlJSRegisterContent content = accumulatorIn.value().content;
3602 m_state.accumulatorVariableIn = m_registerVariables.value(content).variableName;
3603 Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
3605 m_state.accumulatorVariableIn.clear();
3608 auto labelIt = m_labels.constFind(currentInstructionOffset());
3609 if (labelIt != m_labels.constEnd()) {
3610 m_body += *labelIt + u":;\n"_s;
3611 m_skipUntilNextLabel =
false;
3612 }
else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
3613 return SkipInstruction;
3616 if (m_state.changedRegisterIndex() == Accumulator)
3617 m_state.accumulatorVariableOut = changedRegisterVariable();
3619 m_state.accumulatorVariableOut.clear();
3623 Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
3624 || m_state.accumulatorVariableOut.isEmpty());
3625 Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
3626 || !m_state.accumulatorVariableOut.isEmpty()
3627 || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
3631 if (!m_state.hasInternalSideEffects() && changedRegisterVariable().isEmpty()) {
3632 generateJumpCodeWithTypeConversions(0);
3633 return SkipInstruction;
3636 return ProcessInstruction;
3639void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
3641 if (!m_skipUntilNextLabel)
3642 generateJumpCodeWithTypeConversions(0);
3643 m_pool->clearTemporaries();
3646void QQmlJSCodeGenerator::generateSetInstructionPointer()
3648 m_body += u"aotContext->setInstructionPointer("_s
3649 + QString::number(nextInstructionOffset()) + u");\n"_s;
3652void QQmlJSCodeGenerator::generateExceptionCheck()
3654 m_body += u"if (aotContext->engine->hasError()) {\n"_s;
3655 generateReturnError();
3659void QQmlJSCodeGenerator::generateEqualityOperation(
3660 QQmlJSRegisterContent lhsContent, QQmlJSRegisterContent rhsContent,
3661 const QString &lhsName,
const QString &rhsName,
const QString &function,
bool invert)
3663 const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
3664 const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
3666 const QQmlJSScope::ConstPtr rhsContained = rhsIsOptional
3667 ? m_typeResolver->extractNonVoidFromOptionalType(rhsContent).containedType()
3668 : rhsContent.containedType();
3670 const QQmlJSScope::ConstPtr lhsContained = lhsIsOptional
3671 ? m_typeResolver->extractNonVoidFromOptionalType(lhsContent).containedType()
3672 : lhsContent.containedType();
3674 const bool isStrict = function ==
"strictlyEquals"_L1;
3675 const bool strictlyComparableWithVar = isStrict
3676 && QQmlJSUtils::canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
3677 auto isComparable = [&]() {
3678 if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
3680 if (m_typeResolver->isNumeric(lhsContent) && m_typeResolver->isNumeric(rhsContent))
3682 if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
3684 if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
3686 if (strictlyComparableWithVar)
3688 if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
3690 if (QQmlJSUtils::canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
3695 const auto retrieveOriginal = [
this](QQmlJSRegisterContent content) {
3696 const auto contained = content.containedType();
3697 const auto originalContent = originalType(content);
3698 const auto containedOriginal = originalContent.containedType();
3700 if (originalContent.isStoredIn(m_typeResolver->genericType(containedOriginal))) {
3702 return originalContent;
3703 }
else if (contained == containedOriginal) {
3704 if (originalContent.isConversion()) {
3706 return m_pool->storedIn(originalContent, content.storedType());
3708 }
else if (m_typeResolver->canHold(contained, containedOriginal)) {
3709 return m_pool->storedIn(originalContent, content.storedType());
3715 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
3716 const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
3718 if (!isComparable()) {
3719 QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
3720 QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
3721 if (lhsOriginal.containedType() != lhsContent.containedType()
3722 || lhsOriginal.storedType() != lhsType
3723 || rhsOriginal.containedType() != rhsContent.containedType()
3724 || rhsOriginal.storedType() != rhsType) {
3727 generateEqualityOperation(lhsOriginal, rhsOriginal,
3728 conversion(lhsType, lhsOriginal, lhsName),
3729 conversion(rhsType, rhsOriginal, rhsName),
3734 REJECT(u"incomparable types %1 and %2"_s.arg(
3735 rhsContent.descriptiveName(), lhsContent.descriptiveName()));
3738 if (strictlyComparableWithVar) {
3740 if (!lhsName.isEmpty() && rhsName.isEmpty()) {
3742 generateVariantEqualityComparison(rhsContent, lhsName, invert);
3746 if (!rhsName.isEmpty() && lhsName.isEmpty()) {
3748 generateVariantEqualityComparison(lhsContent, rhsName, invert);
3752 if (lhsContent.contains(m_typeResolver->varType())) {
3753 generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
3757 if (rhsContent.contains(m_typeResolver->varType())) {
3758 generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
3764 REJECT(u"comparison of optional null"_s);
3767 const auto comparison = [&]() -> QString {
3768 const auto primitive = m_typeResolver->jsPrimitiveType();
3769 const QString sign = invert ? u" != "_s : u" == "_s;
3771 if (lhsType == rhsType && lhsType != primitive && lhsType != m_typeResolver->varType()) {
3776 if (isTypeStorable(m_typeResolver, lhsType))
3777 return lhsName + sign + rhsName;
3780 return invert ? u"false"_s : u"true"_s;
3783 if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
3785 return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
3787 + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
3790 if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
3797 if (lhsIsOptional) {
3798 if (rhsIsOptional) {
3801 + lhsName + u".isValid() && !"_s
3802 + rhsName + u".isValid()) || "_s;
3805 result += u'(' + lhsName + u".isValid() && "_s;
3810 if (rhsIsOptional) {
3811 result += rhsName + u".isValid() && "_s;
3818 Q_ASSERT(lhsType != m_typeResolver->voidType());
3819 Q_ASSERT(rhsType != m_typeResolver->voidType());
3821 const auto resolvedName = [&](
const QString name) -> QString {
3823 const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
3826 : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
3829 const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
3830 const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
3832 return (invert ? u"!("_s : u"("_s) + result
3833 + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
3835 + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
3839 if ((m_typeResolver->isUnsignedInteger(rhsType)
3840 && m_typeResolver->isUnsignedInteger(lhsType))
3841 || (m_typeResolver->isSignedInteger(rhsType)
3842 && m_typeResolver->isSignedInteger(lhsType))) {
3844 return lhsName + sign + rhsName;
3847 if (rhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(lhsType)) {
3849 return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
3852 if (lhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(rhsType)) {
3854 return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
3857 if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
3859 return convertStored(lhsType, m_typeResolver->realType(), lhsName)
3861 + convertStored(rhsType, m_typeResolver->realType(), rhsName);
3865 return (invert ? u"!"_s : QString())
3866 + convertStored(lhsType, primitive, lhsName)
3867 + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
3870 m_body += m_state.accumulatorVariableOut + u" = "_s;
3871 m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
3875void QQmlJSCodeGenerator::generateCompareOperation(
int lhs,
const QString &cppOperator)
3877 m_body += m_state.accumulatorVariableOut + u" = "_s;
3879 const auto lhsType = registerType(lhs);
3880 const QQmlJSScope::ConstPtr compareType =
3881 m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
3882 ? m_typeResolver->merge(lhsType.storedType(), m_state.accumulatorIn().storedType())
3883 : m_typeResolver->jsPrimitiveType();
3885 m_body += conversion(
3886 m_typeResolver->boolType(), m_state.accumulatorOut(),
3887 convertStored(registerType(lhs).storedType(), compareType,
3888 consumedRegisterVariable(lhs))
3889 + u' ' + cppOperator + u' '
3890 + convertStored(m_state.accumulatorIn().storedType(), compareType,
3891 consumedAccumulatorVariableIn()));
3895void QQmlJSCodeGenerator::generateArithmeticOperation(
int lhs,
const QString &cppOperator)
3897 generateArithmeticOperation(
3898 conversion(registerType(lhs), m_state.readRegister(lhs),
3899 consumedRegisterVariable(lhs)),
3900 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3901 consumedAccumulatorVariableIn()),
3905void QQmlJSCodeGenerator::generateShiftOperation(
int lhs,
const QString &cppOperator)
3907 generateArithmeticOperation(
3908 conversion(registerType(lhs), m_state.readRegister(lhs),
3909 consumedRegisterVariable(lhs)),
3910 u'(' + conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3911 consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
3915void QQmlJSCodeGenerator::generateArithmeticOperation(
3916 const QString &lhs,
const QString &rhs,
const QString &cppOperator)
3918 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhs.isEmpty());
3919 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhs.isEmpty());
3921 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3922 m_body += m_state.accumulatorVariableOut;
3924 const QString explicitCast
3925 = originalOut.isStoredIn(m_typeResolver->stringType())
3926 ? originalOut.storedType()->internalName()
3928 m_body += conversion(
3929 originalOut, m_state.accumulatorOut(),
3930 explicitCast + u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
3934void QQmlJSCodeGenerator::generateArithmeticConstOperation(
int rhsConst,
const QString &cppOperator)
3936 generateArithmeticOperation(
3937 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3938 consumedAccumulatorVariableIn()),
3939 conversion(literalType(m_typeResolver->int32Type()),
3940 m_state.readAccumulator(), QString::number(rhsConst)),
3944void QQmlJSCodeGenerator::generateUnaryOperation(
const QString &cppOperator)
3946 const auto var = conversion(m_state.accumulatorIn(),
3947 originalType(m_state.readAccumulator()),
3948 consumedAccumulatorVariableIn());
3950 if (var == m_state.accumulatorVariableOut) {
3951 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3955 const auto originalResult = originalType(m_state.accumulatorOut());
3956 if (m_state.accumulatorOut() == originalResult) {
3957 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3958 m_body += m_state.accumulatorVariableOut + u" = "_s
3959 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3963 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3964 originalResult, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
3967void QQmlJSCodeGenerator::generateInPlaceOperation(
const QString &cppOperator)
3971 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3972 m_state.accumulatorVariableIn);
3973 if (var == m_state.accumulatorVariableOut) {
3974 m_body += cppOperator + var + u";\n"_s;
3979 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3980 consumedAccumulatorVariableIn());
3982 const auto originalResult = originalType(m_state.accumulatorOut());
3983 if (m_state.accumulatorOut() == originalResult) {
3984 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3985 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3990 m_body += u"auto converted = "_s + var + u";\n"_s;
3991 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3992 originalResult, m_state.accumulatorOut(), u'('
3993 + cppOperator + u"converted)"_s) + u";\n"_s;
3997void QQmlJSCodeGenerator::generateLookup(
const QString &lookup,
const QString &initialization,
3998 const QString &resultPreparation)
4000 m_body += u"#ifndef QT_NO_DEBUG\n"_s;
4001 generateSetInstructionPointer();
4002 m_body += u"#endif\n"_s;
4004 if (!resultPreparation.isEmpty())
4005 m_body += resultPreparation + u";\n"_s;
4006 m_body += u"while (!"_s + lookup + u") {\n"_s;
4008 m_body += u"#ifdef QT_NO_DEBUG\n"_s;
4009 generateSetInstructionPointer();
4010 m_body += u"#endif\n"_s;
4012 m_body += initialization + u";\n"_s;
4013 generateExceptionCheck();
4014 if (!resultPreparation.isEmpty())
4015 m_body += resultPreparation + u";\n"_s;
4019void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset)
4021 QString conversionCode;
4022 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
4023 const auto annotation = m_annotations.find(absoluteOffset);
4024 if (
static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
4025 const auto &conversions = annotation->second.typeConversions;
4027 for (
auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
4028 regIt != regEnd; ++regIt) {
4029 const QQmlJSRegisterContent targetType = regIt.value().content;
4030 if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
4033 const int registerIndex = regIt.key();
4034 const auto variable = m_registerVariables.constFind(targetType);
4036 if (variable == m_registerVariables.constEnd())
4039 QQmlJSRegisterContent currentType;
4040 QString currentVariable;
4041 if (registerIndex == m_state.changedRegisterIndex()) {
4042 currentVariable = changedRegisterVariable();
4043 if (variable->variableName == currentVariable)
4046 currentType = m_state.changedRegister();
4051 const auto it = m_state.registers.find(registerIndex);
4052 if (it == m_state.registers.end()
4053 || variable->variableName == registerVariable(registerIndex)) {
4057 currentType = it.value().content;
4058 currentVariable = consumedRegisterVariable(registerIndex);
4062 if (currentType == targetType)
4065 conversionCode += variable->variableName;
4066 conversionCode += u" = "_s;
4067 conversionCode += conversion(currentType, targetType, currentVariable);
4068 conversionCode += u";\n"_s;
4072 if (relativeOffset) {
4073 auto labelIt = m_labels.find(absoluteOffset);
4074 if (labelIt == m_labels.end())
4075 labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
4076 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
4079 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
4082QString QQmlJSCodeGenerator::registerVariable(
int index)
const
4084 QQmlJSRegisterContent content = registerType(index);
4085 const auto it = m_registerVariables.constFind(content);
4086 if (it != m_registerVariables.constEnd())
4087 return it->variableName;
4092QString QQmlJSCodeGenerator::lookupVariable(
int lookupIndex)
const
4094 for (
auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
4095 if (it.key().resultLookupIndex() == lookupIndex)
4096 return it->variableName;
4101QString QQmlJSCodeGenerator::consumedRegisterVariable(
int index)
const
4103 const QString var = registerVariable(index);
4104 if (var.isEmpty() || !shouldMoveRegister(index))
4106 return u"std::move(" + var + u")";
4109QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn()
const
4111 return shouldMoveRegister(Accumulator)
4112 ? u"std::move(" + m_state.accumulatorVariableIn + u")"
4113 : m_state.accumulatorVariableIn;
4116QString QQmlJSCodeGenerator::changedRegisterVariable()
const
4118 QQmlJSRegisterContent changedRegister = m_state.changedRegister();
4120 const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
4121 if (storedType.isNull())
4124 return m_registerVariables.value(changedRegister).variableName;
4127QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(
int index)
const
4129 auto it = m_state.registers.find(index);
4130 if (it != m_state.registers.end())
4131 return it.value().content;
4133 return QQmlJSRegisterContent();
4136QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(
int lookupIndex)
const
4138 auto it = m_state.lookups.find(lookupIndex);
4139 if (it != m_state.lookups.end())
4140 return it.value().content;
4142 return QQmlJSRegisterContent();
4145bool QQmlJSCodeGenerator::shouldMoveRegister(
int index)
const
4147 return m_state.canMoveReadRegister(index)
4148 && !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
4151QString QQmlJSCodeGenerator::conversion(
4152 QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4154 const QQmlJSScope::ConstPtr contained = to.containedType();
4157 if (from.isStoredIn(m_typeResolver->jsPrimitiveType()) && m_typeResolver->isPrimitive(to)) {
4159 QString primitive = [&]() -> QString {
4160 if (contained == m_typeResolver->jsPrimitiveType())
4163 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
4164 if (contained == m_typeResolver->boolType())
4165 return conversion.arg(u"Boolean"_s);
4166 if (m_typeResolver->isIntegral(to))
4167 return conversion.arg(u"Integer"_s);
4168 if (m_typeResolver->isNumeric(to))
4169 return conversion.arg(u"Double"_s);
4170 if (contained == m_typeResolver->stringType())
4171 return conversion.arg(u"String"_s);
4173 u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
4176 if (primitive.isEmpty())
4179 return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
4182 if (to.isStoredIn(contained)
4183 || m_typeResolver->isNumeric(to.storedType())
4184 || to.storedType()->isReferenceType()
4185 || from.contains(contained)) {
4192 return convertStored(from.storedType(), to.storedType(), variable);
4194 return convertContained(from, to, variable);
4198QString QQmlJSCodeGenerator::convertStored(
4199 const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
const QString &variable)
4203 Q_ASSERT(!to->isComposite());
4205 const auto jsValueType = m_typeResolver->jsValueType();
4206 const auto varType = m_typeResolver->varType();
4207 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
4208 const auto boolType = m_typeResolver->boolType();
4210 auto zeroBoolOrInt = [&](
const QQmlJSScope::ConstPtr &to) {
4213 if (m_typeResolver->isSignedInteger(to))
4215 if (m_typeResolver->isUnsignedInteger(to))
4220 if (from == m_typeResolver->voidType()) {
4221 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4222 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4223 const QString zero = zeroBoolOrInt(to);
4224 if (!zero.isEmpty())
4226 if (to == m_typeResolver->floatType())
4227 return u"std::numeric_limits<float>::quiet_NaN()"_s;
4228 if (to == m_typeResolver->realType())
4229 return u"std::numeric_limits<double>::quiet_NaN()"_s;
4230 if (to == m_typeResolver->stringType())
4231 return QQmlJSUtils::toLiteral(u"undefined"_s);
4232 if (to == m_typeResolver->varType())
4233 return u"QVariant()"_s;
4234 if (to == m_typeResolver->jsValueType())
4235 return u"QJSValue();"_s;
4236 if (to == m_typeResolver->jsPrimitiveType())
4237 return u"QJSPrimitiveValue()"_s;
4242 if (from == m_typeResolver->nullType()) {
4243 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4244 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4245 if (to == jsValueType)
4246 return u"QJSValue(QJSValue::NullValue)"_s;
4247 if (to == jsPrimitiveType)
4248 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
4250 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
4251 const QString zero = zeroBoolOrInt(to);
4252 if (!zero.isEmpty())
4254 if (to == m_typeResolver->floatType())
4256 if (to == m_typeResolver->realType())
4258 if (to == m_typeResolver->stringType())
4259 return QQmlJSUtils::toLiteral(u"null"_s);
4262 REJECT<QString>(u"Conversion from null to %1"_s.arg(to->internalName()));
4268 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4269 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4274 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
4276 if (base->internalName() == to->internalName())
4277 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4279 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
4280 if (base->internalName() == from->internalName())
4281 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4283 }
else if (to == m_typeResolver->boolType()) {
4284 return u'(' + variable + u" != nullptr)"_s;
4288 auto isJsValue = [&](
const QQmlJSScope::ConstPtr &candidate) {
4289 return candidate == jsValueType || candidate->isScript();
4292 if (isJsValue(from) && isJsValue(to))
4295 const auto isBoolOrNumber = [&](
const QQmlJSScope::ConstPtr &type) {
4296 return m_typeResolver->isNumeric(type)
4297 || type == m_typeResolver->boolType()
4298 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
4301 if (from == m_typeResolver->realType() || from == m_typeResolver->floatType()) {
4302 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type()) {
4303 return to->internalName() + u"(QJSNumberCoercion::roundTowards0("_s
4304 + variable + u"))"_s;
4307 if (m_typeResolver->isSignedInteger(to))
4308 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
4309 if (m_typeResolver->isUnsignedInteger(to))
4310 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
4311 if (to == m_typeResolver->boolType())
4312 return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
4315 if (isBoolOrNumber(from) && isBoolOrNumber(to))
4316 return to->internalName() + u'(' + variable + u')';
4319 if (from == jsPrimitiveType) {
4321 return variable + u".toBoolean()"_s;
4322 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type())
4323 return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
4324 if (m_typeResolver->isIntegral(to))
4325 return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
4326 if (m_typeResolver->isNumeric(to))
4327 return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
4328 if (to == m_typeResolver->stringType())
4329 return variable + u".toString()"_s;
4330 if (to == jsValueType)
4331 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
4333 return variable + u".toVariant()"_s;
4334 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4335 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4338 if (isJsValue(from)) {
4339 if (to == jsPrimitiveType)
4340 return variable + u".toPrimitive()"_s;
4342 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
4343 return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
4346 if (to == jsPrimitiveType) {
4348 Q_ASSERT(from != m_typeResolver->nullType());
4349 Q_ASSERT(from != m_typeResolver->voidType());
4351 if (from == m_typeResolver->boolType()
4352 || from == m_typeResolver->int32Type()
4353 || from == m_typeResolver->realType()
4354 || from == m_typeResolver->stringType()) {
4355 return u"QJSPrimitiveValue("_s + variable + u')';
4356 }
else if (from == m_typeResolver->int16Type()
4357 || from == m_typeResolver->int8Type()
4358 || from == m_typeResolver->uint16Type()
4359 || from == m_typeResolver->uint8Type()) {
4360 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
4361 }
else if (m_typeResolver->isNumeric(from)) {
4362 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
4366 if (to == jsValueType)
4367 return u"aotContext->engine->toScriptValue("_s + variable + u')';
4369 if (from == varType) {
4370 if (to == m_typeResolver->listPropertyType())
4371 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
4372 return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
4377 return u"QVariant::fromValue("_s + variable + u')';
4379 if (from == m_typeResolver->urlType() && to == m_typeResolver->stringType())
4380 return variable + u".toString()"_s;
4382 if (from == m_typeResolver->stringType() && to == m_typeResolver->urlType())
4383 return u"QUrl("_s + variable + u')';
4385 if (from == m_typeResolver->byteArrayType() && to == m_typeResolver->stringType())
4386 return u"QString::fromUtf8("_s + variable + u')';
4388 if (from == m_typeResolver->stringType() && to == m_typeResolver->byteArrayType())
4389 return variable + u".toUtf8()"_s;
4391 for (
const auto &originType : {
4392 m_typeResolver->dateTimeType(),
4393 m_typeResolver->dateType(),
4394 m_typeResolver->timeType()}) {
4395 if (from == originType) {
4396 for (
const auto &targetType : {
4397 m_typeResolver->dateTimeType(),
4398 m_typeResolver->dateType(),
4399 m_typeResolver->timeType(),
4400 m_typeResolver->stringType(),
4401 m_typeResolver->realType()}) {
4402 if (to == targetType) {
4403 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
4404 originType->internalName(), targetType->internalName(), variable);
4411 const auto retrieveFromPrimitive = [&](
4412 const QQmlJSScope::ConstPtr &type,
const QString &expression) -> QString
4414 if (type == m_typeResolver->boolType())
4415 return expression + u".toBoolean()"_s;
4416 if (m_typeResolver->isSignedInteger(type))
4417 return expression + u".toInteger()"_s;
4418 if (m_typeResolver->isUnsignedInteger(type))
4419 return u"uint("_s + expression + u".toInteger())"_s;
4420 if (type == m_typeResolver->realType())
4421 return expression + u".toDouble()"_s;
4422 if (type == m_typeResolver->floatType())
4423 return u"float("_s + expression + u".toDouble())"_s;
4424 if (type == m_typeResolver->stringType())
4425 return expression + u".toString()"_s;
4429 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
4430 const QString retrieve = retrieveFromPrimitive(
4431 to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
4432 if (!retrieve.isEmpty())
4436 if (from->isReferenceType() && to == m_typeResolver->stringType()) {
4437 return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
4438 + castTargetName(to) + u">("_s + variable + u')';
4442 if (to == m_typeResolver->boolType()) {
4444 Q_ASSERT(from != m_typeResolver->nullType());
4445 Q_ASSERT(from != m_typeResolver->voidType());
4446 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
4447 Q_ASSERT(!isBoolOrNumber(from));
4452 if (m_typeResolver->areEquivalentLists(from, to))
4455 if (from->isListProperty()
4456 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
4457 && to->elementType()->isReferenceType()
4458 && !to->isListProperty()) {
4459 return variable + u".toList<"_s + to->internalName() + u">()"_s;
4462 bool isExtension =
false;
4463 if (m_typeResolver->canPopulate(to, from, &isExtension)) {
4465 u"populating "_s + to->internalName() + u" from "_s + from->internalName());
4466 }
else if (
const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
4468 const auto argumentTypes = ctor.parameters();
4469 const QString argument = convertStored(from, argumentTypes[0].type(), variable);
4473 return u"[](auto &&arg) { "
4474 "%1 wrapper(std::forward<decltype(arg)>(arg)); "
4475 "return %2(reinterpret_cast<%2 &>(wrapper)); "
4477 to->extensionType().scope->internalName(), to->internalName(), argument);
4480 return u"%1(%2)"_s.arg(to->internalName(), argument);
4483 if (to == m_typeResolver->stringType()
4484 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
4485 addInclude(u"QtQml/qjslist.h"_s);
4489 return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
4496 u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
4499QString QQmlJSCodeGenerator::convertContained(QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4501 const QQmlJSScope::ConstPtr containedFrom = from.containedType();
4502 const QQmlJSScope::ConstPtr containedTo = to.containedType();
4505 Q_ASSERT(!to.storedType()->isReferenceType());
4506 Q_ASSERT(!to.isStoredIn(containedTo));
4507 Q_ASSERT(containedFrom != containedTo);
4509 if (!to.isStoredIn(m_typeResolver->varType())
4510 && !to.isStoredIn(m_typeResolver->jsPrimitiveType())) {
4511 REJECT<QString>(u"internal conversion into unsupported wrapper type."_s);
4514 bool isExtension =
false;
4515 if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
4516 REJECT<QString>(u"populating "_s + containedTo->internalName()
4517 + u" from "_s + containedFrom->internalName());
4518 }
else if (
const auto ctor = m_typeResolver->selectConstructor(
4519 containedTo, containedFrom, &isExtension); ctor.isValid()) {
4520 return generateCallConstructor(
4521 ctor, {from}, {variable}, metaType(containedTo),
4522 metaObject(isExtension ? containedTo->extensionType().scope : containedTo));
4525 const auto originalFrom = originalType(from);
4526 const auto containedOriginalFrom = originalFrom.containedType();
4527 if (containedFrom != containedOriginalFrom
4528 && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
4531 return conversion(m_pool->storedIn(originalFrom, from.storedType()), to, variable);
4534 if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
4535 const QQmlJSRegisterContent intermediate
4536 = m_pool->storedIn(from, m_typeResolver->jsPrimitiveType());
4537 return conversion(intermediate, to, conversion(from, intermediate, variable));
4541 u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
4542 .arg(from.descriptiveName(), to.descriptiveName()));
4545void QQmlJSCodeGenerator::reject(
const QString &thing)
4547 addError(u"Cannot generate efficient code for %1"_s.arg(thing));
4551void QQmlJSCodeGenerator::skip(
const QString &thing)
4553 addSkip(u"Skipped code generation for %1"_s.arg(thing));
4556QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
4557 : accumulatorOut(generator->m_state.accumulatorOut())
4558 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
4559 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
4560 , generator(generator)
4562 if (accumulatorVariableOut.isEmpty())
4565 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
4566 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
4567 const QQmlJSRegisterContent storage = accumulatorOut.storage();
4568 const QQmlJSScope::ConstPtr stored = storage.containedType();
4569 const QQmlJSScope::ConstPtr origStored = resolver->original(storage).containedType();
4574 if (origStored != stored
4575 || (origContained != accumulatorOut.containedType() && stored == resolver->varType())) {
4577 const bool storable = isTypeStorable(resolver, origStored);
4578 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
4579 generator->m_state.setRegister(Accumulator, generator->originalType(accumulatorOut));
4580 generator->m_body += u"{\n"_s;
4582 generator->m_body += origStored->augmentedInternalName() + u' '
4583 + generator->m_state.accumulatorVariableOut + u";\n";
4585 }
else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
4586 && generator->m_state.readsRegister(Accumulator)
4587 && generator->m_state.accumulatorOut().isStoredIn(resolver->varType())) {
4591 generator->m_state.accumulatorVariableIn
4592 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
4593 generator->m_body += u"{\n"_s;
4594 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
4595 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
4599QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
4601 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
4602 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
4603 generator->m_state.accumulatorOut(), accumulatorOut,
4604 u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
4605 generator->m_body += u"}\n"_s;
4606 generator->m_state.setRegister(Accumulator, accumulatorOut);
4607 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
4608 }
else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
4609 generator->m_body += u"}\n"_s;
4610 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
4617QQmlJSCodeGenerator::GeneratePragmaWarningBlock::GeneratePragmaWarningBlock(QQmlJSCodeGenerator *generator)
4618 : m_generator(generator)
4620 m_generator->m_body += u"QT_WARNING_PUSH\n"_s;
4623QQmlJSCodeGenerator::GeneratePragmaWarningBlock::~GeneratePragmaWarningBlock()
4625 m_generator->m_body += u"QT_WARNING_POP\n"_s;
4628void QQmlJSCodeGenerator::GeneratePragmaWarningBlock::silenceDivideByZero()
4632 m_generator->m_body += u"QT_WARNING_DISABLE_MSVC(4723) // potential divide by 0\n"_s;
4635void QQmlJSCodeGenerator::recordPropertyLookup(
const QQmlJSScope::ConstPtr &base,
4636 const QQmlJSMetaProperty &property)
4638 if (!m_noAotValidation) {
4639 if (!m_lookupSignaturesRecorder.recordPropertyLookup(base, property))
4640 REJECT(m_lookupSignaturesRecorder.rejectMessage());
4644void QQmlJSCodeGenerator::recordMethodLookup(
const QQmlJSScope::ConstPtr &base,
4645 const QQmlJSMetaMethod &method)
4647 if (!m_noAotValidation) {
4648 if (!m_lookupSignaturesRecorder.recordMethodLookup(base, method))
4649 REJECT(m_lookupSignaturesRecorder.rejectMessage());
4653void QQmlJSCodeGenerator::recordEnumKeyLookup(
const QQmlJSScope::ConstPtr &base,
4654 const QQmlJSMetaEnum &metaEnum,
4655 const QString &keyName)
4657 if (!m_noAotValidation) {
4658 if (!m_lookupSignaturesRecorder.recordEnumKeyLookup(base, metaEnum, keyName))
4659 REJECT(m_lookupSignaturesRecorder.rejectMessage());
Combined button and popup list for selecting options.
static QString toNumericString(double value)
static QString messageTypeForMethod(const QString &method)
static QString derefContentPointer(const QString &contentPointer)
static bool canTypeBeAffectedBySideEffects(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &baseType)
#define BYTECODE_UNIMPLEMENTED()
#define INJECT_TRACE_INFO(function)
static QString registerName(int registerIndex, int offset)
static QString minExpression(int argc)
static QString maxExpression(int argc)
static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)