Revision control

++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++ // Qt-Security score:critical reason:code-generation
++
++ #include "qqmljscodegenerator_p.h"
++ #include "qqmljsmetatypes_p.h"
++ #include "qqmljsregistercontent_p.h"
++ #include "qqmljsscope_p.h"
++ #include "qqmljsutils_p.h"
++
++ #include <private/qqmljstypepropagator_p.h>
++
++ #include <private/qqmlirbuilder_p.h>
++ #include <private/qqmljsscope_p.h>
++ #include <private/qqmljsutils_p.h>
++ #include <private/qv4compilerscanfunctions_p.h>
++ #include <private/qduplicatetracker_p.h>
++
++ #include <QtCore/qdir.h>
++ #include <QtCore/qfileinfo.h>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ /*!
++ * \internal
++ * \class QQmlJSCodeGenerator
++ *
++ * This is a final compile pass that generates C++ code from a function and the
++ * annotations produced by previous passes. Such annotations are produced by
++ * QQmlJSTypePropagator, and possibly amended by other passes.
++ */
++
++ #define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
++
++ #define INJECT_TRACE_INFO(function) \
++ static const bool injectTraceInfo = true; \
++ if (injectTraceInfo) { \
++ m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \
++ }
++
++ #define REJECT \
++ return reject
++
++ static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
++ {
++ return !type.isNull() && type != resolver->nullType() && type != resolver->voidType();
++ }
++
++ QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const
++ {
++ return type->augmentedInternalName();
++ }
++
++ QQmlJSCodeGenerator::QQmlJSCodeGenerator(
++ const QV4::Compiler::Context *compilerContext,
++ const QV4::Compiler::JSUnitGenerator *unitGenerator, const QQmlJSTypeResolver *typeResolver,
++ QQmlJSLogger *logger, const BasicBlocks &basicBlocks,
++ const InstructionAnnotations &annotations, bool noAotValidation)
++ : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
++ , m_context(compilerContext)
++ , m_lookupSignaturesRecorder(logger->filePath(), typeResolver)
++ , m_noAotValidation(noAotValidation)
++ {}
++
++ QString QQmlJSCodeGenerator::metaTypeFromType(const QQmlJSScope::ConstPtr &type) const
++ {
++ return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
++ }
++
++ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type) const
++ {
++ return u"[]() { static constexpr QByteArrayView name(\""_s
++ + QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
++ + u"\"); static const auto t = QMetaType::fromName(name); return t; }()"_s;
++ }
++
++ QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) const
++ {
++ return u"QQmlPrivate::compositeListMetaType(aotContext->compilationUnit, "_s
++ + (m_jsUnitGenerator->hasStringId(elementName)
++ ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
++ : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
++ }
++
++ QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
++ {
++ return u"QQmlPrivate::compositeMetaType(aotContext->compilationUnit, "_s
++ + (m_jsUnitGenerator->hasStringId(elementName)
++ ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
++ : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
++ }
++
++ QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
++ {
++ if (objectType->isComposite()) {
++ const QString name = m_typeResolver->nameForType(objectType);
++ if (name.isEmpty()) {
++ REJECT<QString>(
++ u"retrieving the metaObject of a composite type without an element name."_s);
++ }
++ return compositeMetaType(name) + u".metaObject()"_s;
++ }
++
++ if (objectType->internalName() == u"QObject"_s
++ || objectType->internalName() == u"QQmlComponent"_s) {
++ return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
++ }
++ return metaTypeFromName(objectType) + u".metaObject()"_s;
++ }
++
++ QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
++ {
++ if (type->isOpaqueType()) {
++ REJECT<QString>("Retrieving the metatype of an unregistered type"_L1);
++ }
++ if (type->isComposite()) {
++ const QString name = m_typeResolver->nameForType(type);
++ if (name.isEmpty()) {
++ REJECT<QString>(
++ u"retrieving the metaType of a composite type without an element name."_s);
++ }
++ return compositeMetaType(name);
++ }
++
++ if (type->isListProperty() && type->elementType()->isComposite()) {
++ const QString name = m_typeResolver->nameForType(type->elementType());
++ Q_ASSERT(!name.isEmpty()); // There can't be a list with anonymous composite value type
++ return compositeListMetaType(name);
++ }
++
++ return (m_typeResolver->genericType(type) == type)
++ ? metaTypeFromType(type)
++ : metaTypeFromName(type);
++ }
++
++ static QString registerName(int registerIndex, int offset)
++ {
++ // 'a<n>': argument number n
++ // 'v<n>_<m>': value produced by instruction for register n at byte code offset m
++ // 'c<n>_<m>': value produced by conversion for register n at byte code offset m
++ // The output of QV4_SHOW_BYTECODE=1 corresponds to the 'm' values.
++ // We can convert a value and then store a new value in the same register with one instruction.
++ // That's why we need both 'v' and 'c'.
++
++ if (offset < 0)
++ return u"s.a%1"_s.arg(registerIndex - QQmlJSCompilePass::Argc);
++ if (registerIndex < 0)
++ return u"s.c%1_%2"_s.arg(-registerIndex).arg(offset);
++ return u"s.v%1_%2"_s.arg(registerIndex).arg(offset);
++ }
++
++ QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function, bool basicBlocksValidationFailed)
++ {
++ m_function = function;
++
++ if (m_context->contextType == QV4::Compiler::ContextType::Binding
++ && m_function->returnType.contains(m_typeResolver->qQmlScriptStringType())) {
++ const QString reason = u"binding for property of type QQmlScriptString; nothing to do."_s;
++ skip(reason);
++ QQmlJSAotFunction result;
++ result.skipReason = reason;
++ return result;
++ }
++
++ const auto addVariable = [&](int registerIndex, int offset, QQmlJSRegisterContent content) {
++ const QQmlJSScope::ConstPtr seenType = content.storedType();
++ // Don't generate any variables for registers that are initialized with undefined.
++ if (!isTypeStorable(m_typeResolver, seenType))
++ return;
++
++ const auto oldSize = m_registerVariables.size();
++ auto &e = m_registerVariables[content];
++ if (m_registerVariables.size() != oldSize) {
++ e.variableName = registerName(registerIndex, offset);
++ e.storedType = seenType;
++ e.initialRegisterIndex = std::abs(registerIndex);
++ } else {
++ e.variableName += u'_' + registerName(registerIndex, offset).mid(2); // skip "s."
++ }
++ ++e.numTracked;
++ };
++
++ QT_WARNING_PUSH
++ QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
++ for (auto it = m_annotations.constBegin(), iend = m_annotations.constEnd(); it != iend; ++it) {
++ const int registerIndex = it->second.changedRegisterIndex;
++ if (registerIndex != InvalidRegister)
++ addVariable(registerIndex, it.key(), it->second.changedRegister);
++ for (auto jt = it->second.typeConversions.constBegin(),
++ jend = it->second.typeConversions.constEnd();
++ jt != jend; ++jt) {
++ // Conversions get a negative register number to discern them.
++ const int registerIndex = jt.key();
++ if (registerIndex != InvalidRegister)
++ addVariable(-registerIndex, it.key(), jt.value().content);
++ }
++ }
++ QT_WARNING_POP
++
++ // ensure we have m_labels for loops
++ for (const auto loopLabel : m_context->labelInfo)
++ m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
++
++ // Initialize the first instruction's state to hold the arguments.
++ // After this, the arguments (or whatever becomes of them) are carried
++ // over into any further basic blocks automatically.
++ m_state.State::operator=(initialState(m_function));
++
++ m_pool->setAllocationMode(QQmlJSRegisterContentPool::Temporary);
++ const QByteArray byteCode = function->code;
++ decode(byteCode.constData(), static_cast<uint>(byteCode.size()));
++ m_pool->setAllocationMode(QQmlJSRegisterContentPool::Permanent);
++
++ QQmlJSAotFunction result;
++ result.includes.swap(m_includes);
++
++ if (basicBlocksValidationFailed) {
++ result.code += "// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
++ "with basic blocks validation but compiled without it.\n"_L1;
++ }
++
++ result.code += u"// %1 at line %2, column %3\n"_s
++ .arg(m_context->name).arg(m_context->line).arg(m_context->column);
++
++ QStringList initializations;
++ QStringList markings;
++ for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
++ registerIt != registerEnd; ++registerIt) {
++
++ // Remove the "s.". Inside the struct we need the plain name.
++ QString declarationName = registerIt->variableName.mid(2);
++
++ const int registerIndex = registerIt->initialRegisterIndex;
++ const bool registerIsArgument = isArgument(registerIndex);
++
++ QString code = registerIt->storedType->internalName();
++
++ const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
++ const bool isPointer
++ = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
++ if (isPointer)
++ code += u" *"_s;
++ else
++ code += u' ';
++
++ if (!registerIsArgument
++ && registerIndex != Accumulator
++ && registerIndex != This
++ && !function->registerTypes[registerIndex - firstRegisterIndex()].contains(
++ m_typeResolver->voidType())) {
++ code += declarationName + u" = "_s;
++ code += convertStored(m_typeResolver->voidType(), storedType, QString());
++ } else if (registerIsArgument && argumentType(registerIndex).isStoredIn(storedType)) {
++ const int argumentIndex = registerIndex - FirstArgument;
++ const QQmlJSRegisterContent argument
++ = m_function->argumentTypes[argumentIndex];
++ const QQmlJSRegisterContent originalArgument = originalType(argument);
++
++ const bool needsConversion = argument != originalArgument;
++ if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
++ // Not a pointer, never written to, and doesn't need any initial conversion.
++ // This is a readonly argument.
++ //
++ // We would like to make the variable a const ref if it's a readonly argument,
++ // but due to the various call interfaces accepting non-const values, we can't.
++ // We rely on those calls to still not modify their arguments in place.
++ code += u'&';
++ }
++
++ code += declarationName + u" = "_s;
++
++ const auto originalContained = m_typeResolver->originalContainedType(argument);
++ QString originalValue;
++ const bool needsQVariantWrapping =
++ storedType->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
++ && !originalContained->isReferenceType()
++ && storedType == m_typeResolver->varType()
++ && originalContained != m_typeResolver->varType();
++ if (needsQVariantWrapping) {
++ originalValue = u"QVariant(%1, argv[%2])"_s.arg(metaTypeFromName(originalContained))
++ .arg(QString::number(argumentIndex + 1));
++ } else {
++ originalValue = u"(*static_cast<"_s + castTargetName(originalArgument.storedType())
++ + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
++ }
++
++ if (needsConversion)
++ code += conversion(originalArgument, argument, originalValue);
++ else
++ code += originalValue;
++ } else if (isPointer) {
++ code += declarationName + u" = nullptr"_s;
++ } else {
++ code += declarationName;
++ }
++ code += u";\n"_s;
++
++ initializations.push_back(std::move(code));
++
++ if (isPointer) {
++ markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
++ } else if (storedType == m_typeResolver->varType()) {
++ markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
++ } else if (storedType == m_typeResolver->listPropertyType()) {
++ // No need to mark that since it's always backed by a property
++ } else if (storedType == m_typeResolver->variantMapType()
++ || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
++ QString marking = u" for (const auto &v : std::as_const(" + declarationName + u"))\n"
++ + u" aotContext->mark(v, markStack);\n";
++ markings.append(std::move(marking));
++ }
++ }
++
++ result.code += u"struct Storage : QQmlPrivate::AOTTrackedLocalsStorage {\n"_s;
++ result.code += u"Storage(const QQmlPrivate::AOTCompiledContext *ctxt, void **a)"_s;
++ result.code += u" : aotContext(ctxt), argv(a) {}\n"_s;
++ result.code += u"void markObjects(QV4::MarkStack *markStack) const final {"_s;
++ result.code += u" Q_UNUSED(markStack);\n"_s;
++
++ markings.sort();
++ for (const QString &marking : std::as_const(markings))
++ result.code += marking;
++
++ result.code += u"}\n"_s;
++ result.code += u"const QQmlPrivate::AOTCompiledContext *aotContext;\n"_s;
++ result.code += u"void **argv;\n"_s;
++
++ // Sort them to obtain stable output.
++ initializations.sort();
++ for (const QString &initialization : std::as_const(initializations))
++ result.code += initialization;
++
++ result.code += u"};\nStorage s(aotContext, argv);\n"_s;
++ result.code += u"aotContext->setLocals(&s);\n"_s;
++
++ result.code += m_body;
++
++
++ QString signature
++ = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { contextUnit };\n"
++ " const auto *aotContext = &c;\n"
++ " Q_UNUSED(aotContext);\n"_s;
++
++ if (function->returnType.isValid()) {
++ signature += u" argTypes[0] = %1;\n"_s.arg(
++ metaType(function->returnType.containedType()));
++ } else {
++ signature += u" argTypes[0] = QMetaType();\n"_s;
++ }
++ result.numArguments = function->argumentTypes.length();
++ for (qsizetype i = 0; i != result.numArguments; ++i) {
++ signature += u" argTypes[%1] = %2;\n"_s.arg(
++ QString::number(i + 1),
++ metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
++ }
++
++ result.signature = std::move(signature);
++ return result;
++ }
++
++ void QQmlJSCodeGenerator::generateReturnError()
++ {
++ const auto finalizeReturn = qScopeGuard([this]() { m_body += u"return;\n"_s; });
++
++ m_body += u"aotContext->setReturnValueUndefined();\n"_s;
++ const auto ret = m_function->returnType;
++ if (!ret.isValid() || ret.contains(m_typeResolver->voidType()))
++ return;
++
++ m_body += u"if (argv[0]) {\n"_s;
++
++ const auto contained = ret.containedType();
++ const auto stored = ret.storedType();
++ if (contained->isReferenceType() && stored->isReferenceType()) {
++ m_body += u" *static_cast<"_s
++ + stored->augmentedInternalName()
++ + u" *>(argv[0]) = nullptr;\n"_s;
++ } else if (contained == stored) {
++ m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
++ + stored->internalName() + u"();\n"_s;
++ } else {
++ m_body += u" const QMetaType returnType = "_s
++ + metaType(ret.containedType()) + u";\n"_s;
++ m_body += u" returnType.destruct(argv[0]);\n"_s;
++ m_body += u" returnType.construct(argv[0]);\n "_s;
++ }
++
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_Ret()
++ {
++ INJECT_TRACE_INFO(generate_Ret);
++
++ const auto finalizeReturn = qScopeGuard([this]() {
++ m_body += u"return;\n"_s;
++ m_skipUntilNextLabel = true;
++ resetState();
++ });
++
++ if (!m_function->returnType.isValid())
++ return;
++
++ m_body += u"if (argv[0]) {\n"_s;
++
++ const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
++ const QString in = m_state.accumulatorVariableIn;
++
++ const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
++
++ if (in.isEmpty()) {
++ if (accumulatorIn.isStoredIn(m_typeResolver->voidType()))
++ m_body += signalUndefined;
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
++ m_body += u" if (!"_s + in + u".isValid())\n"_s;
++ m_body += u" "_s + signalUndefined;
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++ m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
++ m_body += u" "_s + signalUndefined;
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
++ m_body += u" if ("_s + in + u".isUndefined())\n"_s;
++ m_body += u" "_s + signalUndefined;
++ }
++
++ if (m_function->returnType.contains(m_typeResolver->voidType())) {
++ m_body += u"}\n"_s;
++ return;
++ }
++
++ const auto contained = m_function->returnType.containedType();
++ const auto stored = m_function->returnType.storedType();
++ if (contained == stored || (contained->isReferenceType() && stored->isReferenceType())) {
++ // We can always std::move here, no matter what the optimization pass has detected. The
++ // function returns and nothing can access the accumulator register anymore afterwards.
++ m_body += u" *static_cast<"_s
++ + stored->augmentedInternalName()
++ + u" *>(argv[0]) = "_s
++ + conversion(
++ accumulatorIn, m_function->returnType,
++ m_typeResolver->isTriviallyCopyable(accumulatorIn.storedType())
++ ? in
++ : u"std::move("_s + in + u')')
++ + u";\n"_s;
++ } else if (accumulatorIn.contains(contained)) {
++ m_body += u" const QMetaType returnType = "_s + contentType(accumulatorIn, in)
++ + u";\n"_s;
++ m_body += u" returnType.destruct(argv[0]);\n"_s;
++ m_body += u" returnType.construct(argv[0], "_s
++ + contentPointer(accumulatorIn, in) + u");\n"_s;
++ } else {
++ m_body += u" const auto converted = "_s
++ + conversion(accumulatorIn, m_function->returnType,
++ consumedAccumulatorVariableIn()) + u";\n"_s;
++ m_body += u" const QMetaType returnType = "_s
++ + contentType(m_function->returnType, u"converted"_s)
++ + u";\n"_s;
++ m_body += u" returnType.destruct(argv[0]);\n"_s;
++ m_body += u" returnType.construct(argv[0], "_s
++ + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
++ }
++
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_Debug()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ static QString toNumericString(double value)
++ {
++ if (value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max()) {
++ const int i = value;
++ if (i == value)
++ return QString::number(i);
++ }
++
++ switch (qFpClassify(value)) {
++ case FP_INFINITE: {
++ const QString inf = u"std::numeric_limits<double>::infinity()"_s;
++ return std::signbit(value) ? (u'-' + inf) : inf;
++ }
++ case FP_NAN:
++ return u"std::numeric_limits<double>::quiet_NaN()"_s;
++ case FP_ZERO:
++ return std::signbit(value) ? u"-0.0"_s : u"0"_s;
++ default:
++ break;
++ }
++
++ return QString::number(value, 'f', std::numeric_limits<double>::max_digits10);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadConst(int index)
++ {
++ INJECT_TRACE_INFO(generate_LoadConst);
++
++ // You cannot actually get it to generate LoadConst for anything but double. We have
++ // a numer of specialized instructions for the other types, after all. However, let's
++ // play it safe.
++
++ const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
++ const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst);
++ const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst);
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ if (type == m_typeResolver->realType()) {
++ m_body += conversion(
++ type, m_state.accumulatorOut(),
++ toNumericString(value.doubleValue()));
++ } else if (type == m_typeResolver->int32Type()) {
++ m_body += conversion(
++ type, m_state.accumulatorOut(),
++ QString::number(value.integerValue()));
++ } else if (type == m_typeResolver->boolType()) {
++ m_body += conversion(
++ type, m_state.accumulatorOut(),
++ value.booleanValue() ? u"true"_s : u"false"_s);
++ } else if (type == m_typeResolver->voidType()) {
++ m_body += conversion(
++ type, m_state.accumulatorOut(),
++ QString());
++ } else if (type == m_typeResolver->nullType()) {
++ m_body += conversion(
++ type, m_state.accumulatorOut(),
++ u"nullptr"_s);
++ } else {
++ REJECT(u"unsupported constant type"_s);
++ }
++
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadZero()
++ {
++ INJECT_TRACE_INFO(generate_LoadZero);
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s + conversion(
++ m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadTrue()
++ {
++ INJECT_TRACE_INFO(generate_LoadTrue);
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s + conversion(
++ m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadFalse()
++ {
++ INJECT_TRACE_INFO(generate_LoadFalse);
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s + conversion(
++ m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadNull()
++ {
++ INJECT_TRACE_INFO(generate_LoadNull);
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
++ u"nullptr"_s);
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadUndefined()
++ {
++ INJECT_TRACE_INFO(generate_LoadUndefined);
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
++ QString());
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadInt(int value)
++ {
++ INJECT_TRACE_INFO(generate_LoadInt);
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s;
++ m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
++ QString::number(value));
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
++ {
++ INJECT_TRACE_INFO(generate_MoveConst);
++
++ Q_ASSERT(destTemp == m_state.changedRegisterIndex());
++
++ auto var = changedRegisterVariable();
++ if (var.isEmpty())
++ return; // Do not load 'undefined'
++
++ const auto v4Value = QV4::StaticValue::fromReturnedValue(
++ m_jsUnitGenerator->constant(constIndex));
++
++ const auto changed = m_state.changedRegister();
++ QQmlJSScope::ConstPtr contained;
++ QString input;
++
++ m_body += var + u" = "_s;
++ if (v4Value.isNull()) {
++ contained = m_typeResolver->nullType();
++ } else if (v4Value.isUndefined()) {
++ contained = m_typeResolver->voidType();
++ } else if (v4Value.isBoolean()) {
++ contained = m_typeResolver->boolType();
++ input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
++ } else if (v4Value.isInteger()) {
++ contained = m_typeResolver->int32Type();
++ input = QString::number(v4Value.int_32());
++ } else if (v4Value.isDouble()) {
++ contained = m_typeResolver->realType();
++ input = toNumericString(v4Value.doubleValue());
++ } else {
++ REJECT(u"unknown const type"_s);
++ }
++ m_body += conversion(contained, changed, input) + u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadReg(int reg)
++ {
++ INJECT_TRACE_INFO(generate_LoadReg);
++
++ if (registerVariable(reg) == m_state.accumulatorVariableOut)
++ return;
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s;
++ m_body += conversion(
++ registerType(reg), m_state.accumulatorOut(), consumedRegisterVariable(reg));
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreReg(int reg)
++ {
++ INJECT_TRACE_INFO(generate_StoreReg);
++
++ Q_ASSERT(m_state.changedRegisterIndex() == reg);
++ Q_ASSERT(m_state.accumulatorIn().isValid());
++ const QString var = changedRegisterVariable();
++ if (var.isEmpty())
++ return; // don't store "undefined"
++ if (var == m_state.accumulatorVariableIn)
++ return;
++ m_body += var;
++ m_body += u" = "_s;
++ m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
++ consumedAccumulatorVariableIn());
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
++ {
++ INJECT_TRACE_INFO(generate_MoveReg);
++
++ Q_ASSERT(m_state.changedRegisterIndex() == destReg);
++ const QString destRegName = changedRegisterVariable();
++ if (destRegName.isEmpty())
++ return; // don't store things we cannot store.
++ if (destRegName == registerVariable(srcReg))
++ return; // don't emit redundant copy statements
++ m_body += destRegName;
++ m_body += u" = "_s;
++ m_body += conversion(
++ registerType(srcReg), m_state.changedRegister(), consumedRegisterVariable(srcReg));
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadImport(int index)
++ {
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadLocal(int index)
++ {
++ Q_UNUSED(index);
++ REJECT(u"LoadLocal"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreLocal(int index)
++ {
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadScopedLocal(int scope, int index)
++ {
++ Q_UNUSED(scope)
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreScopedLocal(int scope, int index)
++ {
++ Q_UNUSED(scope)
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
++ {
++ INJECT_TRACE_INFO(generate_LoadRuntimeString);
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s;
++ m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
++ QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
++ {
++ Q_UNUSED(regExpId)
++ Q_UNUSED(destReg)
++ REJECT(u"MoveRegExp"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadClosure(int value)
++ {
++ Q_UNUSED(value)
++ REJECT(u"LoadClosure"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadName(int nameIndex)
++ {
++ Q_UNUSED(nameIndex)
++ REJECT(u"LoadName"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
++ {
++ INJECT_TRACE_INFO(generate_LoadGlobalLookup);
++
++ AccumulatorConverter registers(this);
++
++ const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index) + u", "_s
++ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
++ const QString initialization = u"aotContext->initLoadGlobalLookup("_s
++ + QString::number(index) + u", "_s
++ + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
++ const QString preparation = getLookupPreparation(
++ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
++ generateLookup(lookup, initialization, preparation);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
++ {
++ INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
++
++ AccumulatorConverter registers(this);
++
++ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
++
++ const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
++ if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
++ // This produces a QJSValue. The QQmlJSMetaProperty used to analyze it may have more details
++ // but the QQmlJSAotContext API does not reflect them.
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(
++ m_typeResolver->jsValueType(), m_state.accumulatorOut(),
++ u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
++ + u";\n"_s;
++ return;
++ }
++
++ const QString indexString = QString::number(index);
++ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
++ const QString lookup = u"aotContext->loadContextIdLookup("_s
++ + indexString + u", "_s
++ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
++ const QString initialization = u"aotContext->initLoadContextIdLookup("_s
++ + indexString + u')';
++ generateLookup(lookup, initialization);
++ return;
++ }
++
++ const bool isProperty = m_state.accumulatorOut().isProperty();
++ if (isProperty) {
++ const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
++ + indexString + u", "_s
++ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
++ const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
++ + indexString + u')';
++ const QString preparation = getLookupPreparation(
++ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
++
++ recordPropertyLookup(m_typeResolver->original(m_state.accumulatorOut()).scopeType(),
++ originalProperty(m_state.accumulatorOut()));
++ generateLookup(lookup, initialization, preparation);
++ } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
++ generateTypeLookup(index);
++ } else {
++ REJECT(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
++ }
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
++ {
++ INJECT_TRACE_INFO(generate_StoreNameSloppy);
++
++ const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
++ const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
++ Q_ASSERT(type.isProperty());
++
++ switch (type.variant()) {
++ case QQmlJSRegisterContent::Property: {
++ // Do not convert here. We may intentionally pass the "wrong" type, for example to trigger
++ // a property reset.
++ m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
++ + u", "_s
++ + contentPointer(m_state.accumulatorIn(), m_state.accumulatorVariableIn)
++ + u", "_s
++ + contentType(m_state.accumulatorIn(), m_state.accumulatorVariableIn) + u')';
++ m_body += u";\n"_s;
++ break;
++ }
++ case QQmlJSRegisterContent::Method:
++ REJECT(u"assignment to scope method"_s);
++ default:
++ Q_UNREACHABLE();
++ }
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreNameStrict(int name)
++ {
++ Q_UNUSED(name)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadElement(int base)
++ {
++ INJECT_TRACE_INFO(generate_LoadElement);
++
++ const QQmlJSRegisterContent baseType = registerType(base);
++
++ if (!baseType.isList() && !baseType.isStoredIn(m_typeResolver->stringType()))
++ REJECT(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
++
++ const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
++ conversion(literalType(m_typeResolver->voidType()),
++ m_state.accumulatorOut(), QString()) + u";\n"_s;
++
++ AccumulatorConverter registers(this);
++
++ QString indexName = m_state.accumulatorVariableIn;
++ QQmlJSScope::ConstPtr indexType;
++ if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
++ indexType = m_state.accumulatorIn().containedType();
++ } else if (m_state.accumulatorIn().isConversion()) {
++ const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
++ if (m_typeResolver->isNumeric(target)) {
++ indexType = target.containedType();
++ m_body += u"if (!" + indexName + u".metaType().isValid())\n"
++ + voidAssignment
++ + u"else ";
++ indexName = convertStored(
++ m_state.accumulatorIn().storedType(), indexType, indexName);
++ } else {
++ REJECT(u"LoadElement with non-numeric argument"_s);
++ }
++ }
++
++ const QString baseName = registerVariable(base);
++
++ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
++ m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
++ + voidAssignment
++ + u"else "_s;
++ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
++ m_body += u"if ("_s + indexName + u" < 0)\n"_s
++ + voidAssignment
++ + u"else "_s;
++ }
++
++ if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
++ // Our QQmlListProperty only keeps plain QObject*.
++ m_body += u"if ("_s + indexName + u" < "_s + baseName
++ + u".count(&"_s + baseName + u"))\n"_s;
++ m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
++ conversion(m_typeResolver->qObjectType(), m_state.accumulatorOut(),
++ baseName + u".at(&"_s + baseName + u", "_s
++ + indexName + u')') + u";\n"_s;
++ m_body += u"else\n"_s
++ + voidAssignment;
++ return;
++ }
++
++ // Since we can do .at() below, we know that we can natively store the element type.
++ QQmlJSRegisterContent elementType = m_typeResolver->elementType(baseType);
++ elementType = m_pool->storedIn(
++ elementType, m_typeResolver->storedType(elementType.containedType()));
++
++ QString access = baseName + u".at("_s + indexName + u')';
++
++ // TODO: Once we get a char type in QML, use it here.
++ if (baseType.isStoredIn(m_typeResolver->stringType()))
++ access = u"QString("_s + access + u")"_s;
++ else if (isRegisterAffectedBySideEffects(base))
++ REJECT(u"LoadElement on a sequence potentially affected by side effects"_s);
++ else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
++ REJECT(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
++
++ m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
++ m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
++ conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
++ m_body += u"else\n"_s
++ + voidAssignment;
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
++ {
++ INJECT_TRACE_INFO(generate_StoreElement);
++
++ const QQmlJSRegisterContent baseType = registerType(base);
++ const QQmlJSScope::ConstPtr indexType = registerType(index).containedType();
++
++ if (!m_typeResolver->isNumeric(indexType) || !baseType.isList())
++ REJECT(u"StoreElement with non-list base type or non-numeric arguments"_s);
++
++ if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
++ REJECT(u"indirect StoreElement"_s);
++
++ const QString baseName = registerVariable(base);
++ const QString indexName = registerVariable(index);
++
++ const auto elementType = m_typeResolver->genericType(
++ m_typeResolver->elementType(baseType).containedType());
++
++ addInclude(u"QtQml/qjslist.h"_s);
++ if (!m_typeResolver->isNativeArrayIndex(indexType))
++ m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
++ else if (!m_typeResolver->isUnsignedInteger(indexType))
++ m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
++ else
++ m_body += u"{\n"_s;
++
++ if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
++ m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
++ + u"))\n"_s;
++ m_body += u" "_s + baseName + u".replace(&"_s + baseName
++ + u", "_s + indexName + u", "_s;
++ m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
++ + u");\n"_s;
++ m_body += u"}\n"_s;
++ return;
++ }
++
++ if (isRegisterAffectedBySideEffects(base))
++ REJECT(u"StoreElement on a sequence potentially affected by side effects"_s);
++ if (isRegisterAffectedBySideEffects(Accumulator))
++ REJECT(u"StoreElement of a value potentially affected by side effects"_s);
++
++ m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
++ m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
++ + indexName + u" + 1);\n"_s;
++ m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
++ m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
++ + u";\n"_s;
++ m_body += u"}\n"_s;
++
++ generateWriteBack(base);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex)
++ {
++ Q_UNUSED(nameIndex)
++ REJECT(u"LoadProperty"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
++ {
++ Q_UNUSED(name)
++ Q_UNUSED(offset)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generateEnumLookup(int index)
++ {
++ const QString enumMember = m_state.accumulatorOut().enumMember();
++
++ // If we're referring to the type, there's nothing to do.
++ // However, we should not get here since no one can ever use the enum metatype.
++ // The lookup is dead code and should be optimized away.
++ // ... unless you are actually trying to store the metatype itself in a property.
++ // We cannot compile such code.
++ if (enumMember.isEmpty())
++ REJECT(u"Lookup of enum metatype"_s);
++
++ // If the metaenum has the value, just use it and skip all the rest.
++ const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
++ if (metaEnum.hasValues()) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + QString::number(metaEnum.value(enumMember));
++ m_body += u";\n"_s;
++ const QQmlJSRegisterContent &scope = m_state.accumulatorOut().scope();
++ recordEnumKeyLookup(m_typeResolver->originalContainedType(scope), metaEnum, enumMember);
++ return;
++ }
++
++ const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
++
++ // Otherwise we would have found an enum with values.
++ Q_ASSERT(!scopeType->isComposite());
++
++ const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
++ if (enumName.isEmpty()) {
++ if (metaEnum.isFlag() && !metaEnum.name().isEmpty()) {
++ REJECT(u"qmltypes misses name entry for flag; "
++ "did you pass the enum type to Q_FLAG instead of the QFlag type?"
++ "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
++ }
++ REJECT(u"qmltypes misses name entry for enum"_s);
++ }
++ const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
++ + u", &"_s + m_state.accumulatorVariableOut + u')';
++ const QString initialization = u"aotContext->initGetEnumLookup("_s
++ + QString::number(index) + u", "_s + metaObject(scopeType)
++ + u", \""_s + enumName + u"\", \""_s + enumMember
++ + u"\")"_s;
++
++ // No need to record C++ enums lookup as they are resolved at runtime
++ generateLookup(lookup, initialization);
++ }
++
++ void QQmlJSCodeGenerator::generateTypeLookup(int index)
++ {
++ const QString indexString = QString::number(index);
++ const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator).content;
++ const QString namespaceString
++ = accumulatorIn.isImportNamespace()
++ ? QString::number(accumulatorIn.importNamespace())
++ : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
++
++ switch (m_state.accumulatorOut().variant()) {
++ case QQmlJSRegisterContent::Singleton: {
++ rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
++ const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
++ + u", &"_s + m_state.accumulatorVariableOut + u')';
++ const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
++ + u", "_s + namespaceString + u')';
++ generateLookup(lookup, initialization);
++ break;
++ }
++ case QQmlJSRegisterContent::ModulePrefix:
++ break;
++ case QQmlJSRegisterContent::Attachment: {
++ rejectIfNonQObjectOut(u"non-QObject attached type"_s);
++ const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
++ + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
++ const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
++ + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
++ generateLookup(lookup, initialization);
++ break;
++ }
++ case QQmlJSRegisterContent::Script:
++ REJECT(u"script lookup"_s);
++ case QQmlJSRegisterContent::MetaType: {
++ if (!m_state.accumulatorOut().isStoredIn(m_typeResolver->metaObjectType())) {
++ // TODO: Can we trigger this somehow?
++ // It might be impossible, but we better be safe here.
++ REJECT(u"meta-object stored in different type"_s);
++ }
++ const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
++ + u", &"_s + m_state.accumulatorVariableOut + u')';
++ const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
++ + u", "_s + namespaceString + u")"_s;
++ generateLookup(lookup, initialization);
++ break;
++ }
++ default:
++ Q_UNREACHABLE();
++ }
++ }
++
++ void QQmlJSCodeGenerator::generateVariantEqualityComparison(
++ QQmlJSRegisterContent nonStorableContent, const QString &registerName, bool invert)
++ {
++ const auto nonStorableType = nonStorableContent.containedType();
++ QQmlJSScope::ConstPtr comparedType = (nonStorableType == m_typeResolver->nullType())
++ ? m_typeResolver->nullType()
++ : m_typeResolver->voidType();
++
++ // The common operations for both nulltype and voidtype
++ m_body += u"if ("_s + registerName
++ + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
++ + m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
++ u"static_cast<const QJSPrimitiveValue *>("_s + registerName
++ + u".constData())"_s + u"->type() "_s
++ + (invert ? u"!="_s : u"=="_s)
++ + (comparedType == m_typeResolver->nullType()
++ ? u"QJSPrimitiveValue::Null"_s
++ : u"QJSPrimitiveValue::Undefined"_s))
++ + u";\n} else if ("_s + registerName
++ + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
++ + m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
++ (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
++ + registerName + u".constData())"_s + u"->"_s
++ + (comparedType == m_typeResolver->nullType()
++ ? u"isNull()"_s
++ : u"isUndefined()"_s))
++ + u";\n}"_s;
++
++ // Generate nullType specific operations (the case when variant contains QObject * or
++ // std::nullptr_t)
++ if (nonStorableType == m_typeResolver->nullType()) {
++ m_body += u"else if ("_s + registerName
++ + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
++ + m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
++ u"*static_cast<QObject *const *>("_s + registerName
++ + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
++ + u" nullptr"_s)
++ + u";\n} else if ("_s + registerName
++ + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
++ + m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
++ (invert ? u"false"_s : u"true"_s))
++ + u";\n}\n"_s;
++ }
++
++ // fallback case (if variant contains a different type, then it is not null or undefined)
++ m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
++ (invert ? (registerName + u".isValid() ? true : false"_s)
++ : (registerName + u".isValid() ? false : true"_s)))
++ + u";\n}"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateVariantEqualityComparison(
++ QQmlJSRegisterContent storableContent, const QString &typedRegisterName,
++ const QString &varRegisterName, bool invert)
++ {
++ // enumerations are ===-equal to their underlying type and they are stored as such.
++ // Therefore, use the underlying type right away.
++ const QQmlJSScope::ConstPtr contained = storableContent.isEnumeration()
++ ? storableContent.storedType()
++ : storableContent.containedType();
++
++ const QQmlJSScope::ConstPtr boolType = m_typeResolver->boolType();
++ if (contained->isReferenceType()) {
++ const QQmlJSScope::ConstPtr comparable = m_typeResolver->qObjectType();
++ const QString cmpExpr = (invert ? u"!"_s : QString()) + u"(("
++ + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
++ + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
++ + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n";
++ return;
++ }
++
++ if (m_typeResolver->isPrimitive(contained)) {
++ const QQmlJSScope::ConstPtr comparable = m_typeResolver->jsPrimitiveType();
++ const QString cmpExpr = (invert ? u"!"_s : QString())
++ + conversion(storableContent, comparable, typedRegisterName)
++ + u".strictlyEquals("_s
++ + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n"_s;
++ return;
++ }
++
++ REJECT(u"comparison of non-primitive, non-object type to var"_s);
++ }
++
++ void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
++ {
++ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
++ const QQmlJSScope::ConstPtr element = stored->elementType();
++ Q_ASSERT(element);
++
++ QStringList initializer;
++ for (int i = 0; i < argc; ++i) {
++ initializer += convertStored(
++ registerType(argv + i).storedType(), element,
++ consumedRegisterVariable(argv + i));
++ }
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
++ m_body += initializer.join(u", "_s);
++ m_body += u"};\n";
++ }
++
++ void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
++ {
++ QString writeBackRegister = registerVariable(registerIndex);
++ bool writeBackAffectedBySideEffects = isRegisterAffectedBySideEffects(registerIndex);
++
++ for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
++ !writeBack.storedType()->isReferenceType();) {
++
++ switch (writeBack.variant()) {
++ case QQmlJSRegisterContent::Literal:
++ case QQmlJSRegisterContent::Operation:
++ // If the value type or list was produced as a literal or as result
++ // of an operation (like DefineArray ...), we don't have to write back.
++ return;
++ default:
++ break;
++ }
++
++ if (writeBackAffectedBySideEffects)
++ REJECT(u"write-back of value affected by side effects"_s);
++
++ if (writeBack.isConversion())
++ REJECT(u"write-back of converted value"_s);
++
++ const int lookupIndex = writeBack.resultLookupIndex();
++
++ // This is essential for the soundness of the type system.
++ //
++ // If a value or a list is returned from a function, we cannot know
++ // whether it is a copy or a reference. Therefore, we cannot know whether
++ // we have to write it back and so we have to REJECT any write on it.
++ //
++ // Only if we are sure that the value is locally created we can be sure
++ // we don't have to write it back. In this latter case we could allow
++ // a modification that doesn't write back.
++ if (lookupIndex == -1)
++ REJECT(u"write-back of non-lookup"_s);
++
++ const QString writeBackIndexString = QString::number(lookupIndex);
++
++ const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
++ if (variant == QQmlJSRegisterContent::Property && isQmlScopeObject(writeBack.scope())) {
++ const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
++ + writeBackIndexString
++ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
++ const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
++ + writeBackIndexString + u')';
++ recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()),
++ originalProperty(writeBack));
++ generateLookup(lookup, initialization);
++ break;
++ }
++
++
++ QQmlJSRegisterContent outerContent;
++ QString outerRegister;
++ bool outerAffectedBySideEffects = false;
++ for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
++ it != end; ++it) {
++ if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
++ outerContent = it.value().content;
++ outerRegister = lookupVariable(outerContent.resultLookupIndex());
++ outerAffectedBySideEffects = it.value().affectedBySideEffects;
++ break;
++ }
++ }
++
++ // If the lookup doesn't exist, it was killed by state merge.
++ if (!outerContent.isValid())
++ REJECT(u"write-back of lookup across jumps or merges."_s);
++
++ Q_ASSERT(!outerRegister.isEmpty());
++
++ switch (writeBack.variant()) {
++ case QQmlJSRegisterContent::Property: {
++ const QQmlJSMetaProperty property = originalProperty(writeBack);
++ if (writeBack.scopeType()->isReferenceType()) {
++ const QString lookup = u"aotContext->writeBackObjectLookup("_s
++ + writeBackIndexString
++ + u", "_s + outerRegister
++ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
++
++ const QString initialization = (m_state.registers[registerIndex].isShadowable
++ ? u"aotContext->initGetObjectLookupAsVariant("_s
++ : u"aotContext->initGetObjectLookup("_s)
++ + writeBackIndexString + u", "_s + outerRegister + u')';
++ recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()), property);
++ generateLookup(lookup, initialization);
++ } else {
++ const QString valuePointer = contentPointer(outerContent, outerRegister);
++ const QString lookup = u"aotContext->writeBackValueLookup("_s
++ + writeBackIndexString
++ + u", "_s + valuePointer
++ + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
++ const QString initialization = u"aotContext->initGetValueLookup("_s
++ + writeBackIndexString
++ + u", "_s + metaObject(writeBack.scopeType()) + u')';
++ recordPropertyLookup(m_typeResolver->originalContainedType(writeBack.scope()), property);
++ generateLookup(lookup, initialization);
++ }
++ break;
++ }
++ default:
++ REJECT(u"SetLookup on value types (because of missing write-back)"_s);
++ }
++
++ writeBackRegister = outerRegister;
++ writeBack = outerContent;
++ writeBackAffectedBySideEffects = outerAffectedBySideEffects;
++ }
++ }
++
++ void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
++ {
++ if (m_state.accumulatorOut().storedType()->accessSemantics()
++ != QQmlJSScope::AccessSemantics::Reference) {
++ REJECT(error);
++ }
++ }
++
++ void QQmlJSCodeGenerator::rejectIfBadArray()
++ {
++ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
++ if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
++ // This rejects any attempt to store the list into a QVariant.
++ // Therefore, we don't have to adjust the contained type below.
++
++ REJECT(u"storing an array in a non-sequence type"_s);
++ } else if (stored->isListProperty()) {
++ // We can, technically, generate code for this. But it's dangerous:
++ //
++ // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
++ // m_body += stored->internalName() + u"::ListType " + storage
++ // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
++ // m_body += m_state.accumulatorVariableOut
++ // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
++
++ REJECT(u"creating a QQmlListProperty not backed by a property"_s);
++
++ }
++ }
++
++ /*!
++ * \internal
++ *
++ * generates a check for the content pointer to be valid.
++ * Returns true if the content pointer needs to be retrieved from a QVariant, or
++ * false if the variable can be used as-is.
++ */
++ bool QQmlJSCodeGenerator::generateContentPointerCheck(
++ const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
++ const QString &variable, const QString &errorMessage)
++ {
++ const QQmlJSScope::ConstPtr scope = required;
++ const QQmlJSScope::ConstPtr input = actual.containedType();
++ if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
++ [&](const QQmlJSScope::ConstPtr &base) { return base == scope; })) {
++ return false;
++ }
++
++ if (!m_typeResolver->canHold(input, scope)) {
++ REJECT<bool>(u"lookup of members of %1 in %2"_s
++ .arg(scope->internalName(), input->internalName()));
++ }
++
++ bool needsVarContentConversion = false;
++ QString processedErrorMessage;
++ if (actual.storedType()->isReferenceType()) {
++ // Since we have verified the type in qqmljstypepropagator.cpp we now know
++ // that we can only have either null or the actual type here. Therefore,
++ // it's enough to check the pointer for null.
++ m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
++ processedErrorMessage = errorMessage.arg(u"null");
++ } else if (actual.isStoredIn(m_typeResolver->varType())) {
++ // Since we have verified the type in qqmljstypepropagator.cpp we now know
++ // that we can only have either undefined or the actual type here. Therefore,
++ // it's enough to check the QVariant for isValid().
++ m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
++ needsVarContentConversion = true;
++ processedErrorMessage = errorMessage.arg(u"undefined");
++ } else {
++ REJECT<bool>(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
++ }
++
++ generateSetInstructionPointer();
++ m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
++ m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
++ generateReturnError();
++ m_body += u"}\n"_s;
++ return needsVarContentConversion;
++ }
++
++ QString QQmlJSCodeGenerator::generateCallConstructor(
++ const QQmlJSMetaMethod &ctor, const QList<QQmlJSRegisterContent> &argumentTypes,
++ const QStringList &arguments, const QString &metaType, const QString &metaObject)
++ {
++ const auto parameterTypes = ctor.parameters();
++ Q_ASSERT(parameterTypes.length() == argumentTypes.length());
++
++ // We need to store the converted arguments in a temporaries because they might not be lvalues.
++ QStringList argPointers;
++
++ QString result = u"[&](){\n"_s;
++ for (qsizetype i = 0, end = parameterTypes.length(); i < end; ++i) {
++ const QQmlJSRegisterContent argumentType = argumentTypes[i];
++ const QQmlJSScope::ConstPtr parameterType = parameterTypes[i].type();
++ const QString argument = arguments[i];
++ const QString arg = u"arg"_s + QString::number(i);
++
++ result += u" auto "_s + arg + u" = "_s;
++ if (argumentType.contains(parameterType)) {
++ result += argument;
++ argPointers.append(contentPointer(argumentType, arg));
++ } else {
++ const QQmlJSRegisterContent parameterTypeConversion
++ = m_pool->storedIn(
++ m_typeResolver->convert(argumentType, parameterType),
++ m_typeResolver->genericType(parameterType));
++ result += conversion(argumentType, parameterTypeConversion, argument);
++ argPointers.append(contentPointer(parameterTypeConversion, arg));
++ }
++ result += u";\n"_s;
++ }
++
++ result += u" void *args[] = {"_s + argPointers.join(u',') + u"};\n"_s;
++ result += u" return aotContext->constructValueType("_s + metaType + u", "_s + metaObject
++ + u", "_s + QString::number(int(ctor.constructorIndex())) + u", args);\n"_s;
++
++ return result + u"}()"_s;
++ }
++
++ static bool canTypeBeAffectedBySideEffects(
++ const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &baseType)
++ {
++ const QQmlJSScope::ConstPtr contained = baseType.containedType();
++ switch (contained->accessSemantics()) {
++ case QQmlSA::AccessSemantics::Reference:
++ case QQmlSA::AccessSemantics::None:
++ // References and namespaces can't be affected by side effects.
++ return false;
++ case QQmlSA::AccessSemantics::Value:
++ // Value types can have inner objects, and we may have pre-created them where the
++ // interpreter keeps them in JavaScript object form for longer.
++ // TODO: We can probably improve here.
++ return !typeResolver->isPrimitive(contained);
++ case QQmlSA::AccessSemantics::Sequence: {
++ // List properties are never affected by side effects
++ if (contained->isListProperty())
++ return false;
++
++ switch (baseType.variant()) {
++ case QQmlJSRegisterContent::Operation:
++ case QQmlJSRegisterContent::Literal: {
++ // Stack-created lists of primitives and pointers can't be affected by side effects
++ const QQmlJSScope::ConstPtr elementContained = contained->elementType();
++ return !elementContained->isReferenceType()
++ && !typeResolver->isPrimitive(elementContained);
++ }
++ default:
++ return true;
++ }
++ }
++ }
++ return true;
++ }
++
++ bool QQmlJSCodeGenerator::isRegisterAffectedBySideEffects(int registerIndex)
++ {
++ if (!m_state.isRegisterAffectedBySideEffects(registerIndex))
++ return false;
++
++ QQmlJSRegisterContent baseType = registerType(registerIndex);
++ if (baseType.isConversion()) {
++ // A conversion can be affected by side effects if any of its origins can.
++ // Conversions are unrolled on creation, so we don't have to recurse.
++
++ const auto &origins = baseType.conversionOrigins();
++ for (QQmlJSRegisterContent origin : origins) {
++ if (canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(origin)))
++ return true;
++ }
++
++ return false;
++ }
++
++ return canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(baseType));
++ }
++
++ QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
++ const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
++ const QString &variable, const QString &errorMessage)
++ {
++ if (generateContentPointerCheck(required, actual, variable, errorMessage))
++ return variable + u".data()"_s;
++ return contentPointer(actual, variable);
++ }
++
++ QString QQmlJSCodeGenerator::resolveQObjectPointer(
++ const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
++ const QString &variable, const QString &errorMessage)
++ {
++ if (generateContentPointerCheck(required, actual, variable, errorMessage))
++ return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
++ return variable;
++ }
++
++ void QQmlJSCodeGenerator::generate_GetLookup(int index)
++ {
++ INJECT_TRACE_INFO(generate_GetLookup);
++ generate_GetLookupHelper(index);
++ }
++
++ QString QQmlJSCodeGenerator::generateVariantMapGetLookup(
++ const QString &map, const int nameIndex)
++ {
++ const QString mapLookup = map
++ + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
++
++ return m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->varType(), m_state.accumulatorOut(), mapLookup)
++ + u";\n"_s;
++ }
++
++ QString QQmlJSCodeGenerator::generateVariantMapSetLookup(
++ const QString &map, const int nameIndex,
++ const QQmlJSScope::ConstPtr &property, const QString &variableIn)
++ {
++ const QString mapLookup = map
++ + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
++
++ return mapLookup + u" = "_s
++ + conversion(property, m_typeResolver->varType(), variableIn)
++ + u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
++ {
++ if (m_state.accumulatorOut().isMethod())
++ REJECT(u"lookup of function property."_s);
++
++ if (m_state.accumulatorOut().scope().contains(m_typeResolver->mathObject())) {
++ QString name = m_jsUnitGenerator->lookupName(index);
++
++ double value{};
++ if (name == u"E") {
++ value = std::exp(1.0);
++ } else if (name == u"LN10") {
++ value = log(10.0);
++ } else if (name == u"LN2") {
++ value = log(2.0);
++ } else if (name == u"LOG10E") {
++ value = log10(std::exp(1.0));
++ } else if (name == u"LOG2E") {
++ value = log2(std::exp(1.0));
++ } else if (name == u"PI") {
++ value = 3.14159265358979323846;
++ } else if (name == u"SQRT1_2") {
++ value = std::sqrt(0.5);
++ } else if (name == u"SQRT2") {
++ value = std::sqrt(2.0);
++ } else {
++ Q_UNREACHABLE();
++ }
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value))
++ + u";\n"_s;
++ return;
++ }
++
++ if (m_state.accumulatorOut().isImportNamespace()) {
++ Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ModulePrefix);
++ // If we have an object module prefix, we need to pass through the original object.
++ if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
++ m_state.accumulatorVariableIn)
++ + u";\n"_s;
++ }
++ return;
++ }
++
++ AccumulatorConverter registers(this);
++
++ if (m_state.accumulatorOut().isEnumeration()) {
++ generateEnumLookup(index);
++ return;
++ }
++
++ const QString indexString = QString::number(index);
++ const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
++ ? QString::number(m_state.accumulatorIn().importNamespace())
++ : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
++ const auto accumulatorIn = m_state.accumulatorIn();
++ const QQmlJSRegisterContent scope = m_state.accumulatorOut().scope();
++ const QQmlJSRegisterContent originalScope
++ = scope.original().isValid() ? scope.original() : scope;
++ const bool isReferenceType = originalScope.containedType()->isReferenceType();
++
++ switch (m_state.accumulatorOut().variant()) {
++ case QQmlJSRegisterContent::Attachment: {
++ if (isQmlScopeObject(m_state.accumulatorOut().attachee())) {
++ generateTypeLookup(index);
++ return;
++ }
++ if (!isReferenceType) {
++ // This can happen on incomplete type information. We contextually know that the
++ // type must be a QObject, but we cannot construct the inheritance chain. Then we
++ // store it in a generic type. Technically we could even convert it to QObject*, but
++ // that would be expensive.
++ REJECT(u"attached object for non-QObject type"_s);
++ }
++
++ if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
++ // This can happen if we retroactively determine that the property might not be
++ // what we think it is (ie, it can be shadowed).
++ REJECT(u"attached object of potentially non-QObject base"_s);
++ }
++
++ rejectIfNonQObjectOut(u"non-QObject attached type"_s);
++
++ const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
++ + u", "_s + m_state.accumulatorVariableIn
++ + u", &"_s + m_state.accumulatorVariableOut + u')';
++ const QString initialization = u"aotContext->initLoadAttachedLookup("_s
++ + indexString + u", "_s + namespaceString + u", "_s
++ + m_state.accumulatorVariableIn + u')';
++ generateLookup(lookup, initialization);
++ return;
++ }
++ case QQmlJSRegisterContent::Singleton:
++ case QQmlJSRegisterContent::Script:
++ case QQmlJSRegisterContent::MetaType: {
++ generateTypeLookup(index);
++ return;
++ }
++ default:
++ break;
++ }
++
++ Q_ASSERT(m_state.accumulatorOut().isProperty());
++
++ if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
++ REJECT(u"lookup in QJSValue"_s);
++ } else if (isReferenceType) {
++ const QString inputPointer = resolveQObjectPointer(
++ scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
++ u"Cannot read property '%1' of %2"_s.arg(
++ m_jsUnitGenerator->lookupName(index)));
++ const QString lookup = u"aotContext->getObjectLookup("_s + indexString
++ + u", "_s + inputPointer + u", "_s
++ + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
++ const QString initialization = (m_state.isShadowable()
++ ? u"aotContext->initGetObjectLookupAsVariant("_s
++ : u"aotContext->initGetObjectLookup("_s)
++ + indexString + u", "_s + inputPointer + u')';
++ const QString preparation = getLookupPreparation(
++ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
++ recordPropertyLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
++ originalProperty(m_state.accumulatorOut()));
++ generateLookup(lookup, initialization, preparation);
++ } else if ((originalScope.containedType()->accessSemantics()
++ == QQmlJSScope::AccessSemantics::Sequence
++ || originalScope.contains(m_typeResolver->stringType()))
++ && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
++ const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
++ if (stored->isListProperty()) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ m_body += conversion(
++ originalType(m_state.accumulatorOut()),
++ m_state.accumulatorOut(),
++ m_state.accumulatorVariableIn + u".count("_s + u'&'
++ + m_state.accumulatorVariableIn + u')');
++ m_body += u";\n"_s;
++ } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
++ || stored == m_typeResolver->stringType()) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(originalType(m_state.accumulatorOut()),
++ m_state.accumulatorOut(),
++ m_state.accumulatorVariableIn + u".length()"_s)
++ + u";\n"_s;
++ } else if (originalScope.contains(m_typeResolver->stringType())) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(
++ m_typeResolver->sizeType(), m_state.accumulatorOut(),
++ conversion(m_state.accumulatorIn(), m_typeResolver->stringType(),
++ m_state.accumulatorVariableIn)
++ + u".length()"_s)
++ + u";\n"_s;
++ } else {
++ REJECT(u"access to 'length' property of sequence wrapped in non-sequence"_s);
++ }
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->variantMapType())) {
++ m_body += generateVariantMapGetLookup(m_state.accumulatorVariableIn, index);
++ } else {
++ if (m_state.isRegisterAffectedBySideEffects(Accumulator))
++ REJECT(u"reading from a value that's potentially affected by side effects"_s);
++
++ const QString inputContentPointer = resolveValueTypeContentPointer(
++ scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
++ u"Cannot read property '%1' of %2"_s.arg(
++ m_jsUnitGenerator->lookupName(index)));
++
++ if (scope.contains(m_typeResolver->variantMapType())) {
++ m_body += generateVariantMapGetLookup(
++ u"(*static_cast<const QVariantMap *>("_s
++ + inputContentPointer + u"))"_s, index);
++ return;
++ }
++
++ const QString lookup = u"aotContext->getValueLookup("_s + indexString
++ + u", "_s + inputContentPointer
++ + u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
++ + u')';
++ const QString initialization = u"aotContext->initGetValueLookup("_s
++ + indexString + u", "_s
++ + metaObject(scope.containedType()) + u')';
++ const QString preparation = getLookupPreparation(
++ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
++ recordPropertyLookup(m_typeResolver->originalContainedType(scope),
++ originalProperty(m_state.accumulatorOut()));
++ generateLookup(lookup, initialization, preparation);
++ }
++ }
++
++ void QQmlJSCodeGenerator::generate_GetOptionalLookup(int index, int offset)
++ {
++ INJECT_TRACE_INFO(generate_GetOptionalLookup);
++
++ const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
++ QString accumulatorVarIn = m_state.accumulatorVariableIn;
++
++ const auto &annotation = m_annotations[currentInstructionOffset()];
++ if (accumulatorIn.storedType()->isReferenceType()) {
++ m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
++ generateJumpCodeWithTypeConversions(offset);
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
++ m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
++ "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
++ generateJumpCodeWithTypeConversions(offset);
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++ m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
++ "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
++ generateJumpCodeWithTypeConversions(offset);
++ } else if (annotation.changedRegisterIndex == Accumulator
++ && annotation.changedRegister.variant() == QQmlJSRegisterContent::Enum) {
++ // Nothing
++ } else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
++ m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
++ generateJumpCodeWithTypeConversions(offset);
++ } else if (!m_typeResolver->canHoldUndefined(accumulatorIn.storage())) {
++ // The base cannot hold undefined and isn't a reference type, generate a regular get lookup
++ } else {
++ Q_UNREACHABLE(); // No other accumulatorIn stored types should be possible
++ }
++
++ generate_GetLookupHelper(index);
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
++ {
++ Q_UNUSED(nameIndex)
++ Q_UNUSED(baseReg)
++ REJECT(u"StoreProperty"_s);
++ }
++
++ // TODO: This shouldn't be necessary. If the content can be stored directly, then it should
++ // be stored and used directly. If it cannot be stored directly, it should be stored
++ // as QVariant, but then we cannot dereference the content pointer either.
++ static QString derefContentPointer(const QString &contentPointer)
++ {
++ Q_ASSERT(contentPointer.startsWith(u'&') || contentPointer[0].isLetterOrNumber());
++ return contentPointer.startsWith(u'&') ? contentPointer.mid(1) : (u'*' + contentPointer);
++ }
++
++ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
++ {
++ INJECT_TRACE_INFO(generate_SetLookup);
++
++ const QString indexString = QString::number(index);
++ const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
++ const QQmlJSRegisterContent property = m_state.readAccumulator();
++ Q_ASSERT(property.isConversion());
++ const QQmlJSRegisterContent original
++ = m_typeResolver->original(property.conversionResultScope());
++ const QQmlJSScope::ConstPtr originalScope = original.containedType();
++
++ const QString &propertyName = m_jsUnitGenerator->lookupName(index);
++ if (property.storedType().isNull()) {
++ REJECT(u"SetLookup. Could not find property " + propertyName + u" on type "
++ + originalScope->internalName());
++ }
++
++ const QString object = registerVariable(baseReg);
++ m_body += u"{\n"_s;
++ QString variableIn;
++ if (!m_state.accumulatorIn().contains(property.containedType())) {
++ m_body += property.storedType()->augmentedInternalName() + u" converted = "_s
++ + conversion(m_state.accumulatorIn(), property, consumedAccumulatorVariableIn())
++ + u";\n"_s;
++ variableIn = contentPointer(property, u"converted"_s);
++ } else {
++ variableIn = contentPointer(property, m_state.accumulatorVariableIn);
++ }
++
++ switch (originalScope->accessSemantics()) {
++ case QQmlJSScope::AccessSemantics::Reference: {
++ const QString basePointer = resolveQObjectPointer(
++ originalScope, registerType(baseReg), object,
++ u"TypeError: Value is %1 and could not be converted to an object"_s);
++
++ const QString lookup = u"aotContext->setObjectLookup("_s + indexString
++ + u", "_s + basePointer + u", "_s + variableIn + u')';
++
++ // We use the asVariant lookup also for non-shadowable properties if the input can hold
++ // undefined since that may be a reset. See QQmlJSTypePropagator::generate_StoreProperty().
++ const QString initialization
++ = (property.contains(m_typeResolver->varType())
++ ? u"aotContext->initSetObjectLookupAsVariant("_s
++ : u"aotContext->initSetObjectLookup("_s)
++ + indexString + u", "_s + basePointer + u')';
++ recordPropertyLookup(originalScope, originalScope->property(propertyName));
++ generateLookup(lookup, initialization);
++ break;
++ }
++ case QQmlJSScope::AccessSemantics::Sequence: {
++ if (propertyName != u"length"_s)
++ REJECT(u"setting non-length property on a sequence type"_s);
++
++ if (!originalScope->isListProperty())
++ REJECT(u"resizing sequence types (because of missing write-back)"_s);
++
++ // We can resize without write back on a list property because it's actually a reference.
++ m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
++ m_body += u"const int end = "_s + derefContentPointer(variableIn) + u";\n"_s;
++ m_body += u"for (int i = begin; i < end; ++i)\n"_s;
++ m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
++ m_body += u"for (int i = begin; i > end; --i)\n"_s;
++ m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
++ + u";\n"_s;
++ break;
++ }
++ case QQmlJSScope::AccessSemantics::Value: {
++ const QQmlJSRegisterContent base = registerType(baseReg);
++ if (base.isStoredIn(m_typeResolver->variantMapType())) {
++ m_body += generateVariantMapSetLookup(
++ registerVariable(baseReg), index, property.storedType(),
++ derefContentPointer(variableIn));
++ generateWriteBack(baseReg);
++ break;
++ }
++ const QString baseContentPointer = resolveValueTypeContentPointer(
++ originalScope, base, object,
++ u"TypeError: Value is %1 and could not be converted to an object"_s);
++
++ if (original.contains(m_typeResolver->variantMapType())) {
++ m_body += generateVariantMapSetLookup(
++ u"(*static_cast<const QVariantMap *>("_s
++ + baseContentPointer + u"))"_s, index, property.storedType(),
++ derefContentPointer(variableIn));
++ generateWriteBack(baseReg);
++ break;
++ }
++
++ const QString lookup = u"aotContext->setValueLookup("_s + indexString
++ + u", "_s + baseContentPointer
++ + u", "_s + variableIn + u')';
++
++ // We use the asVariant lookup also for non-shadowable properties if the input can hold
++ // undefined since that may be a reset. See QQmlJSTypePropagator::generate_StoreProperty().
++ const QString initialization
++ = (property.contains(m_typeResolver->varType())
++ ? u"aotContext->initSetValueLookupAsVariant("_s
++ : u"aotContext->initSetValueLookup("_s)
++ + indexString + u", "_s + metaObject(originalScope) + u')';
++ recordPropertyLookup(m_typeResolver->originalContainedType(base),
++ originalScope->property(propertyName));
++ generateLookup(lookup, initialization);
++ generateWriteBack(baseReg);
++
++ break;
++ }
++ case QQmlJSScope::AccessSemantics::None:
++ Q_UNREACHABLE();
++ break;
++ }
++
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadSuperProperty(int property)
++ {
++ Q_UNUSED(property)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_StoreSuperProperty(int property)
++ {
++ Q_UNUSED(property)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_Yield()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_YieldStar()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_Resume(int)
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ QString QQmlJSCodeGenerator::initAndCall(
++ int argc, int argv, const QString &callMethodTemplate, const QString &initMethodTemplate,
++ QString *outVar)
++ {
++ QString args;
++
++ if (m_state.changedRegisterIndex() == InvalidRegister ||
++ m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
++ args = u"nullptr"_s;
++ } else {
++ *outVar = u"callResult"_s;
++ const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
++ m_body += outType->augmentedInternalName() + u' ' + *outVar;
++ m_body += u";\n";
++
++ args = contentPointer(m_state.accumulatorOut(), *outVar);
++ }
++
++ // We may need to convert the arguments to the function call so that they match what the
++ // function expects. They are passed as void* after all. We try to convert them where they
++ // are created, but if they are read as different types in multiple places, we can't.
++ QString argumentPreparation;
++ for (int i = 0; i < argc; ++i) {
++ if (isRegisterAffectedBySideEffects(argv + i))
++ REJECT<QString>(u"calling method with argument affected by side effects"_s);
++ const QQmlJSRegisterContent content = registerType(argv + i);
++ const QQmlJSRegisterContent read = m_state.readRegister(argv + i);
++ if (read.contains(content.containedType())) {
++ args += u", "_s + contentPointer(read, registerVariable(argv + i));
++ } else {
++ const QString var = u"arg"_s + QString::number(i);
++ argumentPreparation +=
++ u" "_s + read.storedType()->augmentedInternalName() + u' ' + var + u" = "_s
++ + conversion(content, read, consumedRegisterVariable(argv + i)) + u";\n";
++ args += u", "_s + contentPointer(read, var);
++ }
++ }
++
++ QString initMethod;
++
++ if (m_state.isShadowable()) {
++ initMethod = initMethodTemplate;
++ } else {
++ const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
++ Q_ASSERT(!method.isConstructor());
++
++ initMethod = initMethodTemplate.arg(int(method.methodIndex()));
++ }
++
++ return u"const auto doCall = [&]() {\n"_s
++ + argumentPreparation
++ + u" void *args[] = {" + args + u"};\n"_s
++ + u" return aotContext->"_s + callMethodTemplate.arg(u"args"_s).arg(argc) + u";\n"
++ + u"};\n"_s
++ + u"const auto doInit = [&]() {\n"_s
++ + u" aotContext->"_s + initMethod + u";\n"
++ + u"};\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateMoveOutVarAfterCall(const QString &outVar)
++ {
++ if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
++ return;
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s + u"std::move(" + outVar + u");\n";
++ m_body += u"aotContext->setImplicitDestructible("_s
++ + m_state.accumulatorVariableOut + u");\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_CallValue(int name, int argc, int argv)
++ {
++ Q_UNUSED(name)
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
++ {
++ Q_UNUSED(name)
++ Q_UNUSED(thisObject)
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CallProperty(int nameIndex, int baseReg, int argc, int argv)
++ {
++ Q_UNUSED(nameIndex);
++ Q_UNUSED(baseReg);
++ Q_UNUSED(argc);
++ Q_UNUSED(argv);
++ REJECT(u"CallProperty"_s);
++ }
++
++ bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int argc, int argv)
++ {
++ if (name != u"arg"_s || argc != 1)
++ return false;
++
++ const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
++ return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
++ };
++
++ const auto ret = [&](const QString &arg) {
++ const QString expression = convertStored(
++ registerType(base).storedType(), m_typeResolver->stringType(),
++ consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
++ return conversion(
++ m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
++ };
++
++ const QQmlJSRegisterContent input = m_state.readRegister(argv);
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++
++ if (m_typeResolver->isNumeric(input))
++ m_body += ret(arg(input.containedType()));
++ else if (input.contains(m_typeResolver->boolType()))
++ m_body += ret(arg(m_typeResolver->boolType()));
++ else
++ m_body += ret(arg(m_typeResolver->stringType()));
++ m_body += u";\n"_s;
++ return true;
++ }
++
++ bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
++ {
++ addInclude(u"QtCore/qcoreapplication.h"_s);
++
++ const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
++ Q_ASSERT(i < argc);
++ return convertStored(registerType(argv + i).storedType(), type,
++ consumedRegisterVariable(argv + i));
++ };
++
++ const auto stringArg = [&](int i) {
++ return i < argc
++ ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
++ : u"\"\""_s;
++ };
++
++ const auto intArg = [&](int i) {
++ return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
++ };
++
++ const auto stringRet = [&](const QString &expression) {
++ return conversion(
++ m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
++ };
++
++ const auto capture = [&]() {
++ m_body += u"aotContext->captureTranslation();\n"_s;
++ };
++
++ if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
++ Q_ASSERT(argc > 0);
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"QT_TRANSLATE_NOOP"_s) {
++ Q_ASSERT(argc > 1);
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"qsTrId"_s) {
++ capture();
++ // We inline qtTrId() here because in the !QT_CONFIG(translation) case it's unavailable.
++ // QCoreApplication::translate() is always available in some primitive form.
++ // Also, this saves a function call.
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
++ u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"qsTr"_s) {
++ capture();
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + stringRet(u"QCoreApplication::translate("_s
++ + u"aotContext->translationContext().toUtf8().constData(), "_s
++ + stringArg(0) + u", "_s + stringArg(1) + u", "_s
++ + intArg(2) + u")"_s) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"qsTranslate"_s) {
++ capture();
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + stringRet(u"QCoreApplication::translate("_s
++ + stringArg(0) + u", "_s + stringArg(1) + u", "_s
++ + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
++ return true;
++ }
++
++ return false;
++ }
++
++ static QString maxExpression(int argc)
++ {
++ Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
++
++ QString expression =
++ u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
++ for (int i = 2; i < argc; i++) {
++ expression +=
++ "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
++ .arg("arg"_L1 + QString::number(i + 1));
++ }
++ expression += "return tmpMax;\n}()"_L1;
++
++ return expression;
++ }
++
++ static QString minExpression(int argc)
++ {
++ Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
++
++ QString expression =
++ u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
++ for (int i = 2; i < argc; i++) {
++ expression +=
++ "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
++ .arg("arg"_L1 + QString::number(i + 1));
++ }
++ expression += "return tmpMin;\n}()"_L1;
++
++ return expression;
++ }
++
++ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
++ {
++ addInclude(u"cmath"_s);
++ addInclude(u"limits"_s);
++ addInclude(u"QtCore/qalgorithms.h"_s);
++ addInclude(u"QtCore/qrandom.h"_s);
++ addInclude(u"QtQml/qjsprimitivevalue.h"_s);
++
++ // If the result is not stored, we don't need to generate any code. All the math methods are
++ // conceptually pure functions.
++ if (m_state.changedRegisterIndex() != Accumulator)
++ return true;
++
++ m_body += u"{\n"_s;
++ for (int i = 0; i < argc; ++i) {
++ m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
++ registerType(argv + i).storedType(),
++ m_typeResolver->realType(), consumedRegisterVariable(argv + i))
++ + u";\n"_s;
++ }
++
++ const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
++ const QString inf = u"std::numeric_limits<double>::infinity()"_s;
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++
++ QString expression;
++
++ if (name == u"abs" && argc == 1) {
++ expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
++ } else if (name == u"acos"_s && argc == 1) {
++ expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
++ } else if (name == u"acosh"_s && argc == 1) {
++ expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
++ } else if (name == u"asin"_s && argc == 1) {
++ expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
++ } else if (name == u"asinh"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
++ } else if (name == u"atan"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
++ } else if (name == u"atanh"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
++ } else if (name == u"atan2"_s) {
++ // TODO: complicated
++ return false;
++ } else if (name == u"cbrt"_s && argc == 1) {
++ expression = u"std::cbrt(arg1)"_s;
++ } else if (name == u"ceil"_s && argc == 1) {
++ expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
++ } else if (name == u"clz32"_s && argc == 1) {
++ expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
++ } else if (name == u"cos"_s && argc == 1) {
++ expression = u"std::cos(arg1)"_s;
++ } else if (name == u"cosh"_s && argc == 1) {
++ expression = u"std::cosh(arg1)"_s;
++ } else if (name == u"exp"_s && argc == 1) {
++ expression = u"std::isinf(arg1) "
++ "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
++ ": std::exp(arg1)"_s.arg(inf);
++ } else if (name == u"expm1"_s) {
++ // TODO: complicated
++ return false;
++ } else if (name == u"floor"_s && argc == 1) {
++ expression = u"std::floor(arg1)"_s;
++ } else if (name == u"fround"_s && argc == 1) {
++ expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
++ "? arg1 "
++ ": double(float(arg1))"_s;
++ } else if (name == u"hypot"_s) {
++ // TODO: complicated
++ return false;
++ } else if (name == u"imul"_s && argc == 2) {
++ expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
++ "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
++ } else if (name == u"log"_s && argc == 1) {
++ expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
++ } else if (name == u"log10"_s && argc == 1) {
++ expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
++ } else if (name == u"log1p"_s && argc == 1) {
++ expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
++ } else if (name == u"log2"_s && argc == 1) {
++ expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
++ } else if (name == u"max"_s && argc >= 2) {
++ expression = maxExpression(argc);
++ } else if (name == u"min"_s && argc >= 2) {
++ expression = minExpression(argc);
++ } else if (name == u"pow"_s) {
++ expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
++ } else if (name == u"random"_s && argc == 0) {
++ expression = u"QRandomGenerator::global()->generateDouble()"_s;
++ } else if (name == u"round"_s && argc == 1) {
++ expression = u"std::isfinite(arg1) "
++ "? ((arg1 < 0.5 && arg1 >= -0.5) "
++ "? std::copysign(0.0, arg1) "
++ ": std::floor(arg1 + 0.5)) "
++ ": arg1"_s;
++ } else if (name == u"sign"_s && argc == 1) {
++ expression = u"std::isnan(arg1) "
++ "? %1 "
++ ": (qIsNull(arg1) "
++ "? arg1 "
++ ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
++ } else if (name == u"sin"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
++ } else if (name == u"sinh"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
++ } else if (name == u"sqrt"_s && argc == 1) {
++ expression = u"std::sqrt(arg1)"_s;
++ } else if (name == u"tan"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
++ } else if (name == u"tanh"_s && argc == 1) {
++ expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
++ } else if (name == u"trunc"_s && argc == 1) {
++ expression = u"std::trunc(arg1)"_s;
++ } else {
++ return false;
++ }
++
++ m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
++
++ m_body += u";\n"_s;
++ m_body += u"}\n"_s;
++ return true;
++ }
++
++ static QString messageTypeForMethod(const QString &method)
++ {
++ if (method == u"log" || method == u"debug")
++ return u"QtDebugMsg"_s;
++ if (method == u"info")
++ return u"QtInfoMsg"_s;
++ if (method == u"warn")
++ return u"QtWarningMsg"_s;
++ if (method == u"error")
++ return u"QtCriticalMsg"_s;
++ return QString();
++ }
++
++ bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int argv)
++ {
++ const QString type = messageTypeForMethod(name);
++ if (type.isEmpty())
++ return false;
++
++ addInclude(u"QtCore/qloggingcategory.h"_s);
++
++ m_body += u"{\n";
++ m_body += u" bool firstArgIsCategory = false;\n";
++ const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
++
++ // We could check whether the first argument is a QQmlLoggingCategoryBase here, and we should
++ // because QQmlLoggingCategoryBase is now a builtin.
++ // TODO: The run time check for firstArg is obsolete.
++ const bool firstArgIsReference = argc > 0
++ && firstArg.containedType()->isReferenceType();
++
++ if (firstArgIsReference) {
++ m_body += u" QObject *firstArg = ";
++ m_body += convertStored(
++ firstArg.storedType(),
++ m_typeResolver->genericType(firstArg.storedType()),
++ registerVariable(argv));
++ m_body += u";\n";
++ }
++
++ m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
++ m_body += firstArgIsReference ? u"firstArg"_sv : u"nullptr"_sv;
++ m_body += u", &firstArgIsCategory);\n";
++ m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
++
++ m_body += u" const QString message = ";
++
++ const auto stringConversion = [&](int i) -> QString {
++ const QQmlJSScope::ConstPtr read = m_state.readRegister(argv + i).storedType();
++ const QQmlJSScope::ConstPtr actual = registerType(argv + i).storedType();
++ if (read == m_typeResolver->stringType()) {
++ return convertStored(actual, read, consumedRegisterVariable(argv + i));
++ } else if (actual->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
++ addInclude(u"QtQml/qjslist.h"_s);
++ return u"(u'[' + QJSList(&"_s + registerVariable(argv + i)
++ + u", aotContext->engine).toString() + u']')"_s;
++ } else {
++ REJECT<QString>(u"converting arguments for console method to string"_s);
++ }
++ };
++
++ if (argc > 0) {
++ if (firstArgIsReference) {
++ const QString firstArgStringConversion = convertStored(
++ registerType(argv).storedType(),
++ m_typeResolver->stringType(), registerVariable(argv));
++ m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
++ if (argc > 1)
++ m_body += u".append(QLatin1Char(' ')))).append(";
++ else
++ m_body += u"))";
++ } else {
++ m_body += stringConversion(0);
++ if (argc > 1)
++ m_body += u".append(QLatin1Char(' ')).append(";
++ }
++
++ for (int i = 1; i < argc; ++i) {
++ if (i > 1)
++ m_body += u".append(QLatin1Char(' ')).append("_s;
++ m_body += stringConversion(i) + u')';
++ }
++ } else {
++ m_body += u"QString()";
++ }
++ m_body += u";\n ";
++ generateSetInstructionPointer();
++ m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
++ m_body += u" }\n";
++ m_body += u"}\n";
++ return true;
++ }
++
++ bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
++ {
++ const auto intType = m_typeResolver->int32Type();
++ const auto elementType = registerType(base).storedType()->elementType();
++ const auto boolType = m_typeResolver->boolType();
++ const auto stringType = m_typeResolver->stringType();
++ const auto baseType = registerType(base);
++
++ const QString baseVar = registerVariable(base);
++ const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
++ + name + u"(";
++
++ addInclude(u"QtQml/qjslist.h"_s);
++
++ if (name == u"includes" && argc > 0 && argc < 3) {
++ QString call = qjsListMethod
++ + convertStored(registerType(argv).storedType(), elementType,
++ consumedRegisterVariable(argv));
++ if (argc == 2) {
++ call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
++ consumedRegisterVariable(argv + 1));
++ }
++ call += u")";
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"toString" || (name == u"join" && argc < 2)) {
++ QString call = qjsListMethod;
++ if (argc == 1) {
++ call += convertStored(registerType(argv).storedType(), stringType,
++ consumedRegisterVariable(argv));
++ }
++ call += u")";
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
++ return true;
++ }
++
++ if (name == u"slice" && argc < 3) {
++ QString call = qjsListMethod;
++ for (int i = 0; i < argc; ++i) {
++ if (i > 0)
++ call += u", ";
++ call += convertStored(registerType(argv + i).storedType(), intType,
++ consumedRegisterVariable(argv + i));
++ }
++ call += u")";
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ if (baseType.storedType()->isListProperty())
++ m_body += conversion(m_typeResolver->qObjectListType(), m_state.accumulatorOut(), call);
++ else
++ m_body += conversion(baseType, m_state.accumulatorOut(), call);
++ m_body += u";\n"_s;
++
++ return true;
++ }
++
++ if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
++ QString call = qjsListMethod
++ + convertStored(registerType(argv).storedType(), elementType,
++ consumedRegisterVariable(argv));
++ if (argc == 2) {
++ call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
++ consumedRegisterVariable(argv + 1));
++ }
++ call += u")";
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
++ return true;
++ }
++
++ return false;
++ }
++
++ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int argc, int argv)
++ {
++ INJECT_TRACE_INFO(generate_CallPropertyLookup);
++
++ const QQmlJSRegisterContent scopeContent = m_state.accumulatorOut().scope();
++ const QQmlJSScope::ConstPtr scope = scopeContent.containedType();
++
++ AccumulatorConverter registers(this);
++
++ const QQmlJSRegisterContent baseType = registerType(base);
++ const QString name = m_jsUnitGenerator->lookupName(index);
++
++ if (scope == m_typeResolver->mathObject()) {
++ if (inlineMathMethod(name, argc, argv))
++ return;
++ } else if (scope == m_typeResolver->consoleObject()) {
++ if (inlineConsoleMethod(name, argc, argv))
++ return;
++ } else if (scope == m_typeResolver->stringType()) {
++ if (inlineStringMethod(name, base, argc, argv))
++ return;
++ } else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
++ if (inlineArrayMethod(name, base, argc, argv))
++ return;
++ }
++
++ if (m_state.accumulatorOut().isJavaScriptReturnValue())
++ REJECT(u"call to untyped JavaScript function"_s);
++
++ m_body += u"{\n"_s;
++ QString outVar;
++
++ if (scope->isReferenceType()) {
++ const QString inputPointer = resolveQObjectPointer(
++ scope, baseType, registerVariable(base),
++ u"Cannot call method '%1' of %2"_s.arg(name));
++
++ const QString initMethodTemplate = m_state.isShadowable()
++ ? u"initCallObjectPropertyLookupAsVariant(%1, %2)"_s
++ : u"initCallObjectPropertyLookup(%1, %2, %3)"_s;
++
++ m_body += initAndCall(
++ argc, argv,
++ u"callObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
++ initMethodTemplate.arg(index).arg(inputPointer), &outVar);
++ } else {
++ const QQmlJSScope::ConstPtr originalScope
++ = m_typeResolver->original(scopeContent).containedType();
++ const QString inputPointer = resolveValueTypeContentPointer(
++ originalScope, baseType, registerVariable(base),
++ u"Cannot call method '%1' of %2"_s.arg(name));
++
++ m_body += initAndCall(
++ argc, argv,
++ u"callValueLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
++ u"initCallValueLookup(%1, %2, %3)"_s
++ .arg(index).arg(metaObject(originalScope)),
++ &outVar);
++ }
++
++ const QString lookup = u"doCall()"_s;
++ const QString initialization = u"doInit()"_s;
++ const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
++ recordMethodLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
++ originalMethod(m_state.accumulatorOut()));
++ generateLookup(lookup, initialization, preparation);
++ generateMoveOutVarAfterCall(outVar);
++
++ m_body += u"}\n"_s;
++
++ if (scope->isReferenceType())
++ return;
++
++ const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
++ if (!method.isConst())
++ generateWriteBack(base);
++ }
++
++ void QQmlJSCodeGenerator::generate_CallName(int name, int argc, int argv)
++ {
++ Q_UNUSED(name);
++ Q_UNUSED(argc);
++ Q_UNUSED(argv);
++ REJECT(u"CallName"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(int argc, int argv)
++ {
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CallGlobalLookup(int index, int argc, int argv)
++ {
++ Q_UNUSED(index);
++ Q_UNUSED(argc);
++ Q_UNUSED(argv);
++ REJECT(u"CallGlobalLookup"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int argc, int argv)
++ {
++ INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
++
++ if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
++ const QString name = m_jsUnitGenerator->stringForIndex(
++ m_jsUnitGenerator->lookupNameIndex(index));
++ if (inlineTranslateMethod(name, argc, argv))
++ return;
++ }
++
++ if (m_state.accumulatorOut().isJavaScriptReturnValue())
++ REJECT(u"call to untyped JavaScript function"_s);
++
++ AccumulatorConverter registers(this);
++
++ m_body += u"{\n"_s;
++ QString outVar;
++ m_body += initAndCall(
++ argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
++ u"initCallQmlContextPropertyLookup(%1, %2)"_s.arg(index), &outVar);
++
++ const QString lookup = u"doCall()"_s;
++ const QString initialization = u"doInit()"_s;
++ const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
++ recordMethodLookup(m_typeResolver->originalContainedType(m_state.accumulatorOut().scope()),
++ originalMethod(m_state.accumulatorOut()));
++ generateLookup(lookup, initialization, preparation);
++ generateMoveOutVarAfterCall(outVar);
++
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
++ {
++ Q_UNUSED(func)
++ Q_UNUSED(thisObject)
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc, int argv)
++ {
++ Q_UNUSED(func)
++ Q_UNUSED(thisObject)
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
++ {
++ INJECT_TRACE_INFO(generate_Construct);
++ Q_UNUSED(func);
++
++ const auto originalResult = originalType(m_state.accumulatorOut());
++
++ if (originalResult.contains(m_typeResolver->dateTimeType())) {
++ m_body += m_state.accumulatorVariableOut + u" = ";
++ if (argc == 0) {
++ m_body += conversion(
++ m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
++ u"QDateTime::currentDateTime()"_s) + u";\n";
++ return;
++ }
++
++ if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->dateTimeType())) {
++ m_body += conversion(
++ registerType(argv), m_state.readRegister(argv), registerVariable(argv))
++ + u";\n";
++ return;
++ }
++
++ QString ctorArgs;
++ constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
++ for (int i = 0; i < std::min(argc, maxArgc); ++i) {
++ if (i > 0)
++ ctorArgs += u", ";
++ ctorArgs += conversion(
++ registerType(argv + i), m_state.readRegister(argv + i),
++ registerVariable(argv + i));
++ }
++ m_body += conversion(
++ m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
++ u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
++ return;
++ }
++
++ if (originalResult.contains(m_typeResolver->variantListType())) {
++ rejectIfBadArray();
++
++ if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->realType())) {
++ addInclude(u"QtQml/qjslist.h"_s);
++
++ const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
++ + u"QLatin1String(\"Invalid array length\"));\n"_s;
++
++ const QString indexName = registerVariable(argv);
++ const auto indexType = registerType(argv).containedType();
++ if (!m_typeResolver->isNativeArrayIndex(indexType)) {
++ m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
++ + error;
++ generateReturnError();
++ m_body += u"}\n"_s;
++ } else if (!m_typeResolver->isUnsignedInteger(indexType)) {
++ m_body += u"if ("_s + indexName + u" < 0) {\n"_s
++ + error;
++ generateReturnError();
++ m_body += u"}\n"_s;
++ }
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
++ m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
++ + u", aotContext->engine).resize("_s
++ + convertStored(
++ registerType(argv).storedType(), m_typeResolver->sizeType(),
++ consumedRegisterVariable(argv))
++ + u");\n"_s;
++ } else if (!m_logger->currentFunctionHasCompileError()) {
++ generateArrayInitializer(argc, argv);
++ }
++ return;
++ }
++
++ const QQmlJSScope::ConstPtr originalContained = originalResult.containedType();
++ if (originalContained->isValueType() && originalResult.isMethodCall()) {
++ const QQmlJSMetaMethod ctor = originalResult.methodCall();
++ if (ctor.isJavaScriptFunction())
++ REJECT(u"calling JavaScript constructor "_s + ctor.methodName());
++
++ QList<QQmlJSRegisterContent> argumentTypes;
++ QStringList arguments;
++ for (int i = 0; i < argc; ++i) {
++ argumentTypes.append(registerType(argv + i));
++ arguments.append(consumedRegisterVariable(argv + i));
++ }
++
++ const QQmlJSScope::ConstPtr extension = originalContained->extensionType().scope;
++ const QString result = generateCallConstructor(
++ ctor, argumentTypes, arguments, metaType(originalContained),
++ metaObject(extension ? extension : originalContained));
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + conversion(m_pool->storedIn(originalResult, m_typeResolver->varType()),
++ m_state.accumulatorOut(), result)
++ + u";\n"_s;
++
++ return;
++ }
++
++
++ REJECT(u"Construct"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_ConstructWithSpread(int func, int argc, int argv)
++ {
++ Q_UNUSED(func)
++ Q_UNUSED(argc)
++ Q_UNUSED(argv)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_SetUnwindHandler(int offset)
++ {
++ Q_UNUSED(offset)
++ REJECT(u"SetUnwindHandler"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UnwindDispatch()
++ {
++ REJECT(u"UnwindDispatch"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UnwindToLabel(int level, int offset)
++ {
++ Q_UNUSED(level)
++ Q_UNUSED(offset)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(int name)
++ {
++ Q_UNUSED(name)
++ INJECT_TRACE_INFO(generate_DeadTemporalZoneCheck);
++ // Nothing to do here. If we have statically asserted the dtz check in the type propagator
++ // the value cannot be empty. Otherwise we can't get here.
++ }
++
++ void QQmlJSCodeGenerator::generate_ThrowException()
++ {
++ INJECT_TRACE_INFO(generate_ThrowException);
++
++ generateSetInstructionPointer();
++ m_body += u"aotContext->engine->throwError("_s + conversion(
++ m_state.accumulatorIn(),
++ m_typeResolver->jsValueType(),
++ m_state.accumulatorVariableIn) + u");\n"_s;
++ generateReturnError();
++ m_skipUntilNextLabel = true;
++ resetState();
++ }
++
++ void QQmlJSCodeGenerator::generate_GetException()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_SetException()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CreateCallContext()
++ {
++ INJECT_TRACE_INFO(generate_CreateCallContext);
++
++ m_body += u"{\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_PushCatchContext(int index, int nameIndex)
++ {
++ Q_UNUSED(index)
++ Q_UNUSED(nameIndex)
++ REJECT(u"PushCatchContext"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_PushWithContext()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_PushBlockContext(int index)
++ {
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CloneBlockContext()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_PushScriptContext(int index)
++ {
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_PopScriptContext()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_PopContext()
++ {
++ INJECT_TRACE_INFO(generate_PopContext);
++
++ // Add an empty block before the closing brace, in case there was a bare label before it.
++ m_body += u"{}\n}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_GetIterator(int iterator)
++ {
++ INJECT_TRACE_INFO(generate_GetIterator);
++
++ addInclude(u"QtQml/qjslist.h"_s);
++ const QQmlJSRegisterContent listType = m_state.accumulatorIn();
++ if (!listType.isList())
++ REJECT(u"iterator on non-list type"_s);
++
++ const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
++ if (!iteratorType.isProperty())
++ REJECT(u"using non-iterator as iterator"_s);
++
++ const QString identifier = QString::number(iteratorType.baseLookupIndex());
++ QString baseName = m_state.accumulatorVariableOut.mid(2); // remove "s."
++ const QString iteratorName = baseName + u"Iterator" + identifier;
++ const QString listName = baseName + u"List" + identifier;
++
++ m_body += u"QJSListFor"_s
++ + (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
++ + u"Iterator "_s + iteratorName + u";\n";
++ m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
++
++ m_body += m_state.accumulatorVariableOut + u"->init(";
++ if (iterator == int(QQmlJS::AST::ForEachType::In)) {
++ if (!iteratorType.isStoredIn(m_typeResolver->forInIteratorPtr()))
++ REJECT(u"using non-iterator as iterator"_s);
++ m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
++ }
++ m_body += u");\n";
++
++ if (iterator == int(QQmlJS::AST::ForEachType::Of)) {
++ if (!iteratorType.isStoredIn(m_typeResolver->forOfIteratorPtr()))
++ REJECT(u"using non-iterator as iterator"_s);
++ m_body += u"const auto &" // Rely on life time extension for const refs
++ + listName + u" = " + consumedAccumulatorVariableIn();
++ }
++ }
++
++ void QQmlJSCodeGenerator::generate_IteratorNext(int value, int offset)
++ {
++ INJECT_TRACE_INFO(generate_IteratorNext);
++
++ Q_ASSERT(value == m_state.changedRegisterIndex());
++ const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
++ if (!iteratorContent.isProperty())
++ REJECT(u"using non-iterator as iterator"_s);
++
++ const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
++ const QString listName = m_state.accumulatorVariableIn
++ + u"List" + QString::number(iteratorContent.baseLookupIndex());
++ QString qjsList;
++ if (iteratorType == m_typeResolver->forOfIteratorPtr())
++ qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
++ else if (iteratorType != m_typeResolver->forInIteratorPtr())
++ REJECT(u"using non-iterator as iterator"_s);
++
++ m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
++
++ // We know that this works because we can do ->next() below.
++ QQmlJSRegisterContent iteratorValue = m_typeResolver->extractNonVoidFromOptionalType(
++ m_typeResolver->original(m_state.changedRegister()));
++ iteratorValue = m_pool->storedIn(iteratorValue, iteratorValue.containedType());
++
++ m_body += changedRegisterVariable() + u" = "
++ + conversion(
++ iteratorValue, m_state.changedRegister(),
++ m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
++ + u";\n";
++ m_body += u"} else {\n ";
++ m_body += changedRegisterVariable() + u" = "
++ + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
++ m_body += u";\n ";
++ generateJumpCodeWithTypeConversions(offset);
++ m_body += u"\n}"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
++ {
++ Q_UNUSED(iterator)
++ Q_UNUSED(object)
++ Q_UNUSED(offset)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_IteratorClose()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_DestructureRestElement()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_DeleteProperty(int base, int index)
++ {
++ Q_UNUSED(base)
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_DeleteName(int name)
++ {
++ Q_UNUSED(name)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_TypeofName(int name)
++ {
++ Q_UNUSED(name);
++ REJECT(u"TypeofName"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_TypeofValue()
++ {
++ REJECT(u"TypeofValue"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
++ {
++ Q_UNUSED(varName)
++ Q_UNUSED(isDeletable)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
++ {
++ INJECT_TRACE_INFO(generate_DefineArray);
++
++ rejectIfBadArray();
++ if (!m_logger->currentFunctionHasCompileError())
++ generateArrayInitializer(argc, args);
++ }
++
++ void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
++ {
++ INJECT_TRACE_INFO(generate_DefineObjectLiteral);
++
++ const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
++ if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value)
++ REJECT(u"storing an object literal in a non-value type"_s);
++
++ const QQmlJSScope::ConstPtr contained = m_state.accumulatorOut().containedType();
++
++ const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
++
++ // This is not implemented because we cannot statically determine the type of the value and we
++ // don't want to rely on QVariant::convert() since that may give different results than
++ // the JavaScript coercion. We might still make it work by querying the QMetaProperty
++ // for its type at run time and runtime coercing to that, but we don't know whether that
++ // still pays off.
++ if (argc > classSize)
++ REJECT(u"non-literal keys of object literals"_s);
++
++ Q_ASSERT(argc == classSize);
++
++ const auto createVariantMap = [&]() {
++ QString result;
++ result += u"QVariantMap {\n";
++ const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
++ for (int i = 0; i < classSize; ++i) {
++ result += u"{ "_s
++ + QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
++ + u", "_s;
++ const int currentArg = args + i;
++ const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
++ const QString consumedArg = consumedRegisterVariable(currentArg);
++ result += convertStored(argType, propType, consumedArg) + u" },\n";
++ }
++
++
++ result += u"}";
++ return result;
++ };
++
++ if (contained == m_typeResolver->varType() || contained == m_typeResolver->variantMapType()) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s + createVariantMap() + u";\n"_s;
++ return;
++ }
++
++ if (contained == m_typeResolver->jsValueType()) {
++ m_body += m_state.accumulatorVariableOut + u" = aotContext->engine->toScriptValue("_s
++ + createVariantMap() + u");\n"_s;
++ return;
++ }
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
++ const bool isVariantOrPrimitive = (stored == m_typeResolver->varType())
++ || (stored == m_typeResolver->jsPrimitiveType());
++
++ if (m_state.accumulatorOut().contains(stored)) {
++ m_body += u"()";
++ } else if (isVariantOrPrimitive) {
++ m_body += u'(' + metaType(m_state.accumulatorOut().containedType()) + u')';
++ } else {
++ REJECT(u"storing an object literal in an unsupported container %1"_s
++ .arg(stored->internalName()));
++ }
++ m_body += u";\n";
++
++ if (argc == 0)
++ return;
++
++ bool isExtension = false;
++ if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension))
++ REJECT(u"storing an object literal in a non-structured value type"_s);
++
++ const QQmlJSScope::ConstPtr accessor = isExtension
++ ? contained->extensionType().scope
++ : contained;
++
++ m_body += u"{\n";
++ m_body += u" const QMetaObject *meta = ";
++ if (!isExtension && isVariantOrPrimitive)
++ m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
++ else
++ m_body += metaObject(accessor);
++ m_body += u";\n";
++
++ for (int i = 0; i < classSize; ++i) {
++ m_body += u" {\n";
++ const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
++ const int currentArg = args + i;
++ const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
++ const QQmlJSRegisterContent argType = registerType(currentArg);
++ const QQmlJSMetaProperty property = contained->property(propName);
++ const QString consumedArg = consumedRegisterVariable(currentArg);
++ QString argument = conversion(argType, propType, consumedArg);
++
++ if (argument == consumedArg) {
++ argument = registerVariable(currentArg);
++ } else {
++ m_body += u" "_s + propType.storedType()->augmentedInternalName()
++ + u" arg = "_s + argument + u";\n";
++ argument = u"arg"_s;
++ }
++
++ int index = property.index();
++ if (index == -1)
++ continue;
++
++ const QString indexString = QString::number(index);
++ m_body += u" void *argv[] = { %1, nullptr };\n"_s
++ .arg(contentPointer(propType, argument));
++ m_body += u" meta->property("_s + indexString;
++ m_body += u").enclosingMetaObject()->d.static_metacall(reinterpret_cast<QObject *>(";
++ m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
++ m_body += u"), QMetaObject::WriteProperty, " + indexString + u", argv);\n";
++ m_body += u" }\n";
++ }
++
++ m_body += u"}\n";
++
++ }
++
++ void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
++ {
++ Q_UNUSED(classIndex)
++ Q_UNUSED(heritage)
++ Q_UNUSED(computedNames)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CreateRestParameter(int argIndex)
++ {
++ Q_UNUSED(argIndex)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_ConvertThisToObject()
++ {
++ INJECT_TRACE_INFO(generate_ConvertThisToObject);
++
++ m_body += changedRegisterVariable() + u" = "_s
++ + conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
++ u"aotContext->thisObject()"_s)
++ + u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_ToObject()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_Jump(int offset)
++ {
++ INJECT_TRACE_INFO(generate_Jump);
++
++ generateJumpCodeWithTypeConversions(offset);
++ m_skipUntilNextLabel = true;
++ resetState();
++ }
++
++ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
++ {
++ INJECT_TRACE_INFO(generate_JumpTrue);
++
++ m_body += u"if ("_s;
++ m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
++ m_state.accumulatorVariableIn);
++ m_body += u") "_s;
++ generateJumpCodeWithTypeConversions(offset);
++ }
++
++ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
++ {
++ INJECT_TRACE_INFO(generate_JumpFalse);
++
++ m_body += u"if (!"_s;
++ m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
++ m_state.accumulatorVariableIn);
++ m_body += u") "_s;
++ generateJumpCodeWithTypeConversions(offset);
++ }
++
++ void QQmlJSCodeGenerator::generate_JumpNoException(int offset)
++ {
++ INJECT_TRACE_INFO(generate_JumpNoException);
++
++ m_body += u"if (!context->engine->hasException()) "_s;
++ generateJumpCodeWithTypeConversions(offset);
++ }
++
++ void QQmlJSCodeGenerator::generate_JumpNotUndefined(int offset)
++ {
++ Q_UNUSED(offset)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_CheckException()
++ {
++ INJECT_TRACE_INFO(generate_CheckException);
++
++ generateExceptionCheck();
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpEqNull()
++ {
++ INJECT_TRACE_INFO(generate_CmpEqNull);
++ generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s, false);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpNeNull()
++ {
++ INJECT_TRACE_INFO(generate_CmlNeNull);
++ generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s, true);
++ }
++
++ QString QQmlJSCodeGenerator::getLookupPreparation(
++ QQmlJSRegisterContent content, const QString &var, int lookup)
++ {
++ if (content.contains(content.storedType()))
++ return QString();
++
++ if (content.storedType()->isOpaqueType()) {
++ // Can't do meta-type lookups if the type is potentially unregistered,
++ // which can be the case for mehods
++ REJECT<QString>("Can't prepare lookup involving non-registered types"_L1);
++ }
++
++ if (content.isStoredIn(m_typeResolver->varType())) {
++ return var + u" = QVariant(aotContext->lookupResultMetaType("_s
++ + QString::number(lookup) + u"))"_s;
++ }
++
++ if (content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++ return var + u" = QJSPrimitiveValue(aotContext->lookupResultMetaType("_s
++ + QString::number(lookup) + u"))"_s;
++ }
++
++ // TODO: We could make sure they're compatible, for example QObject pointers.
++ return QString();
++ }
++
++ QString QQmlJSCodeGenerator::contentPointer(QQmlJSRegisterContent content, const QString &var)
++ {
++ const QQmlJSScope::ConstPtr stored = content.storedType();
++ if (content.contains(stored))
++ return u'&' + var;
++
++ if (content.isStoredIn(m_typeResolver->varType())
++ || content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++ return var + u".data()"_s;
++ }
++
++ if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
++ return u'&' + var;
++
++ if (m_typeResolver->isNumeric(content.storedType())
++ && content.containedType()->scopeType() == QQmlSA::ScopeType::EnumScope) {
++ return u'&' + var;
++ }
++
++ if (stored->isListProperty() && content.containedType()->isListProperty())
++ return u'&' + var;
++
++ REJECT<QString>(
++ u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
++ }
++
++ QString QQmlJSCodeGenerator::contentType(QQmlJSRegisterContent content, const QString &var)
++ {
++ const QQmlJSScope::ConstPtr stored = content.storedType();
++ const QQmlJSScope::ConstPtr contained = content.containedType();
++ if (contained == stored)
++ return metaTypeFromType(stored);
++
++ if (stored == m_typeResolver->varType() || stored == m_typeResolver->jsPrimitiveType())
++ return var + u".metaType()"_s; // We expect the container to be initialized
++
++ if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
++ return metaType(contained);
++
++ const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
++ if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
++ return metaTypeFromType(nonComposite->baseType());
++
++ if (stored->isListProperty() && contained->isListProperty())
++ return metaType(contained);
++
++ REJECT<QString>(
++ u"content type of unsupported wrapper type "_s + content.descriptiveName());
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
++ {
++ INJECT_TRACE_INFO(generate_CmpEqInt);
++
++ generateEqualityOperation(
++ literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s, false);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
++ {
++ INJECT_TRACE_INFO(generate_CmpNeInt);
++
++ generateEqualityOperation(
++ literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s, true);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpEq);
++ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, false);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpNe(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpNe);
++ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s, true);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpGt(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpGt);
++ generateCompareOperation(lhs, u">"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpGe(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpGe);
++ generateCompareOperation(lhs, u">="_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpLt(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpLt);
++ generateCompareOperation(lhs, u"<"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpLe(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpLe);
++ generateCompareOperation(lhs, u"<="_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpStrictEqual(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpStrictEqual);
++ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, false);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_CmpStrictNotEqual);
++ generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s, true);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpIn(int lhs)
++ {
++ Q_UNUSED(lhs)
++ REJECT(u"CmpIn"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_CmpInstanceOf(int lhs)
++ {
++ Q_UNUSED(lhs)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_As(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_As);
++
++ const QString input = registerVariable(lhs);
++ const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
++ const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
++
++ // If the originalType output is a conversion, we're supposed to check for the contained
++ // type and if it doesn't match, set the result to null or undefined.
++ const QQmlJSRegisterContent originalContent = originalType(outputContent);
++ QQmlJSScope::ConstPtr target;
++ if (originalContent.containedType()->isReferenceType())
++ target = originalContent.containedType();
++ else if (originalContent.isConversion())
++ target = m_typeResolver->extractNonVoidFromOptionalType(originalContent).containedType();
++ else if (originalContent.variant() == QQmlJSRegisterContent::Cast)
++ target = originalContent.containedType();
++
++ if (!target)
++ REJECT(u"type assertion to unknown type"_s);
++
++ const bool isTrivial = m_typeResolver->inherits(
++ m_typeResolver->originalContainedType(inputContent), target);
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++
++ if (!isTrivial && target->isReferenceType()) {
++ const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
++ const QString inputConversion = inputContent.storedType()->isReferenceType()
++ ? input
++ : convertStored(inputContent.storedType(), genericContained, input);
++
++ if (target->isComposite()
++ && m_state.accumulatorIn().isStoredIn(m_typeResolver->metaObjectType())) {
++ m_body += conversion(
++ genericContained, outputContent,
++ m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
++ } else {
++ m_body += conversion(
++ genericContained, outputContent,
++ u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
++ }
++ m_body += u";\n"_s;
++ return;
++ }
++
++ if (inputContent.isStoredIn(m_typeResolver->varType())
++ || inputContent.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++
++ const auto source = m_typeResolver->extractNonVoidFromOptionalType(
++ originalType(inputContent)).containedType();
++
++ if (source && source == target) {
++ m_body += input + u".metaType() == "_s + metaType(target)
++ + u" ? " + conversion(inputContent, outputContent, input)
++ + u" : " + conversion(
++ literalType(m_typeResolver->voidType()), outputContent, QString());
++ m_body += u";\n"_s;
++ return;
++ }
++ }
++
++ if (isTrivial) {
++ // No actual conversion necessary. The 'as' is a no-op
++ m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
++ return;
++ }
++
++ REJECT(u"non-trivial value type assertion"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UNot()
++ {
++ INJECT_TRACE_INFO(generate_UNot);
++ generateUnaryOperation(u"!"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UPlus()
++ {
++ INJECT_TRACE_INFO(generate_UPlus);
++ generateUnaryOperation(u"+"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UMinus()
++ {
++ INJECT_TRACE_INFO(generate_UMinus);
++ generateUnaryOperation(u"-"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UCompl()
++ {
++ INJECT_TRACE_INFO(generate_UCompl);
++ generateUnaryOperation(u"~"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Increment()
++ {
++ INJECT_TRACE_INFO(generate_Increment);
++ generateInPlaceOperation(u"++"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Decrement()
++ {
++ INJECT_TRACE_INFO(generate_Decrement);
++ generateInPlaceOperation(u"--"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Add(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Add);
++ generateArithmeticOperation(lhs, u"+"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitAnd(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_BitAnd);
++ generateArithmeticOperation(lhs, u"&"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitOr(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_BitOr);
++ generateArithmeticOperation(lhs, u"|"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitXor(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_BitXor);
++ generateArithmeticOperation(lhs, u"^"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UShr(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_BitUShr);
++ generateShiftOperation(lhs, u">>"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Shr(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Shr);
++ generateShiftOperation(lhs, u">>"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Shl(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Shl);
++ generateShiftOperation(lhs, u"<<"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitAndConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_BitAndConst);
++ generateArithmeticConstOperation(rhs, u"&"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitOrConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_BitOrConst);
++ generateArithmeticConstOperation(rhs, u"|"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_BitXorConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_BitXorConst);
++ generateArithmeticConstOperation(rhs, u"^"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_UShrConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_UShrConst);
++ generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_ShrConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_ShrConst);
++ generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_ShlConst(int rhs)
++ {
++ INJECT_TRACE_INFO(generate_ShlConst);
++ generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Exp(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Exp);
++
++ const QString lhsString = conversion(
++ registerType(lhs), m_state.readRegister(lhs), consumedRegisterVariable(lhs));
++ const QString rhsString = conversion(
++ m_state.accumulatorIn(), m_state.readAccumulator(),
++ consumedAccumulatorVariableIn());
++
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsString.isEmpty());
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsString.isEmpty());
++
++ const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ m_body += conversion(
++ originalOut, m_state.accumulatorOut(),
++ u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_Mul(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Mul);
++ generateArithmeticOperation(lhs, u"*"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Div(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Div);
++ GeneratePragmaWarningBlock warningBlock(this);
++ warningBlock.silenceDivideByZero();
++ generateArithmeticOperation(lhs, u"/"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_Mod(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Mod);
++
++ const auto lhsVar = convertStored(
++ registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
++ consumedRegisterVariable(lhs));
++ const auto rhsVar = convertStored(
++ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
++ consumedAccumulatorVariableIn());
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsVar.isEmpty());
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsVar.isEmpty());
++
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s;
++ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
++ u'(' + lhsVar + u" % "_s + rhsVar + u')');
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generate_Sub(int lhs)
++ {
++ INJECT_TRACE_INFO(generate_Sub);
++ generateArithmeticOperation(lhs, u"-"_s);
++ }
++
++ void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
++ {
++ Q_UNUSED(firstReg)
++ Q_UNUSED(count)
++ // Ignore. We reject uninitialized values anyway.
++ }
++
++ void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
++ {
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ void QQmlJSCodeGenerator::generate_GetTemplateObject(int index)
++ {
++ Q_UNUSED(index)
++ BYTECODE_UNIMPLEMENTED();
++ }
++
++ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
++ QV4::Moth::Instr::Type type)
++ {
++ m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
++ const auto accumulatorIn = m_state.registers.find(Accumulator);
++ if (accumulatorIn != m_state.registers.end()
++ && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
++ QQmlJSRegisterContent content = accumulatorIn.value().content;
++ m_state.accumulatorVariableIn = m_registerVariables.value(content).variableName;
++ Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
++ } else {
++ m_state.accumulatorVariableIn.clear();
++ }
++
++ auto labelIt = m_labels.constFind(currentInstructionOffset());
++ if (labelIt != m_labels.constEnd()) {
++ m_body += *labelIt + u":;\n"_s;
++ m_skipUntilNextLabel = false;
++ } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
++ return SkipInstruction;
++ }
++
++ if (m_state.changedRegisterIndex() == Accumulator)
++ m_state.accumulatorVariableOut = changedRegisterVariable();
++ else
++ m_state.accumulatorVariableOut.clear();
++
++ // If the accumulator type is valid, we want an accumulator variable.
++ // If not, we don't want one.
++ Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
++ || m_state.accumulatorVariableOut.isEmpty());
++ Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
++ || !m_state.accumulatorVariableOut.isEmpty()
++ || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
++
++ // If the instruction has no side effects and doesn't write any register, it's dead.
++ // We might still need the label, though, and the source code comment.
++ if (!m_state.hasInternalSideEffects() && changedRegisterVariable().isEmpty()) {
++ generateJumpCodeWithTypeConversions(0);
++ return SkipInstruction;
++ }
++
++ return ProcessInstruction;
++ }
++
++ void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
++ {
++ if (!m_skipUntilNextLabel)
++ generateJumpCodeWithTypeConversions(0);
++ m_pool->clearTemporaries();
++ }
++
++ void QQmlJSCodeGenerator::generateSetInstructionPointer()
++ {
++ m_body += u"aotContext->setInstructionPointer("_s
++ + QString::number(nextInstructionOffset()) + u");\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateExceptionCheck()
++ {
++ m_body += u"if (aotContext->engine->hasError()) {\n"_s;
++ generateReturnError();
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateEqualityOperation(
++ QQmlJSRegisterContent lhsContent, QQmlJSRegisterContent rhsContent,
++ const QString &lhsName, const QString &rhsName, const QString &function, bool invert)
++ {
++ const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
++ const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
++
++ const QQmlJSScope::ConstPtr rhsContained = rhsIsOptional
++ ? m_typeResolver->extractNonVoidFromOptionalType(rhsContent).containedType()
++ : rhsContent.containedType();
++
++ const QQmlJSScope::ConstPtr lhsContained = lhsIsOptional
++ ? m_typeResolver->extractNonVoidFromOptionalType(lhsContent).containedType()
++ : lhsContent.containedType();
++
++ const bool isStrict = function == "strictlyEquals"_L1;
++ const bool strictlyComparableWithVar = isStrict
++ && QQmlJSUtils::canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
++ auto isComparable = [&]() {
++ if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
++ return true;
++ if (m_typeResolver->isNumeric(lhsContent) && m_typeResolver->isNumeric(rhsContent))
++ return true;
++ if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
++ return true;
++ if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
++ return true;
++ if (strictlyComparableWithVar)
++ return true;
++ if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
++ return true;
++ if (QQmlJSUtils::canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
++ return true;
++ return false;
++ };
++
++ const auto retrieveOriginal = [this](QQmlJSRegisterContent content) {
++ const auto contained = content.containedType();
++ const auto originalContent = originalType(content);
++ const auto containedOriginal = originalContent.containedType();
++
++ if (originalContent.isStoredIn(m_typeResolver->genericType(containedOriginal))) {
++ // The original type doesn't need any wrapping.
++ return originalContent;
++ } else if (contained == containedOriginal) {
++ if (originalContent.isConversion()) {
++ // The original conversion origins are more accurate
++ return m_pool->storedIn(originalContent, content.storedType());
++ }
++ } else if (m_typeResolver->canHold(contained, containedOriginal)) {
++ return m_pool->storedIn(originalContent, content.storedType());
++ }
++
++ return content;
++ };
++
++ const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
++ const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
++
++ if (!isComparable()) {
++ QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
++ QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
++ if (lhsOriginal.containedType() != lhsContent.containedType()
++ || lhsOriginal.storedType() != lhsType
++ || rhsOriginal.containedType() != rhsContent.containedType()
++ || rhsOriginal.storedType() != rhsType) {
++ // If either side is simply a wrapping of a specific type into a more general one, we
++ // can compare the original types instead. You can't nest wrappings after all.
++ generateEqualityOperation(lhsOriginal, rhsOriginal,
++ conversion(lhsType, lhsOriginal, lhsName),
++ conversion(rhsType, rhsOriginal, rhsName),
++ function, invert);
++ return;
++ }
++
++ REJECT(u"incomparable types %1 and %2"_s.arg(
++ rhsContent.descriptiveName(), lhsContent.descriptiveName()));
++ }
++
++ if (strictlyComparableWithVar) {
++ // Determine which side is holding a storable type
++ if (!lhsName.isEmpty() && rhsName.isEmpty()) {
++ // lhs register holds var type and rhs is not storable
++ generateVariantEqualityComparison(rhsContent, lhsName, invert);
++ return;
++ }
++
++ if (!rhsName.isEmpty() && lhsName.isEmpty()) {
++ // lhs content is not storable and rhs is var type
++ generateVariantEqualityComparison(lhsContent, rhsName, invert);
++ return;
++ }
++
++ if (lhsContent.contains(m_typeResolver->varType())) {
++ generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
++ return;
++ }
++
++ if (rhsContent.contains(m_typeResolver->varType())) {
++ generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
++ return;
++ }
++
++ // It shouldn't be possible to get here because optional null should be stored in
++ // QJSPrimitiveValue, not in QVariant. But let's rather be safe than sorry.
++ REJECT(u"comparison of optional null"_s);
++ }
++
++ const auto comparison = [&]() -> QString {
++ const auto primitive = m_typeResolver->jsPrimitiveType();
++ const QString sign = invert ? u" != "_s : u" == "_s;
++
++ if (lhsType == rhsType && lhsType != primitive && lhsType != m_typeResolver->varType()) {
++
++ // Straight forward comparison of equal types,
++ // except QJSPrimitiveValue which has two comparison functions.
++
++ if (isTypeStorable(m_typeResolver, lhsType))
++ return lhsName + sign + rhsName;
++
++ // null === null and undefined === undefined
++ return invert ? u"false"_s : u"true"_s;
++ }
++
++ if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
++ // Comparison of QObject-derived with nullptr or different QObject-derived.
++ return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
++ + sign
++ + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
++ }
++
++ if (QQmlJSUtils::canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
++ // Comparison of optional QObject-derived with nullptr or different QObject-derived.
++ // Mind that null == undefined but null !== undefined
++ // Therefore the isStrict dance.
++
++ QString result;
++ if (isStrict) {
++ if (lhsIsOptional) {
++ if (rhsIsOptional) {
++ // If both are invalid we're fine
++ result += u"(!"_s
++ + lhsName + u".isValid() && !"_s
++ + rhsName + u".isValid()) || "_s;
++ }
++
++ result += u'(' + lhsName + u".isValid() && "_s;
++ } else {
++ result += u'(';
++ }
++
++ if (rhsIsOptional) {
++ result += rhsName + u".isValid() && "_s;
++ }
++ } else {
++ result += u'(';
++ }
++
++ // We do not implement comparison with explicit undefined, yet. Only with null.
++ Q_ASSERT(lhsType != m_typeResolver->voidType());
++ Q_ASSERT(rhsType != m_typeResolver->voidType());
++
++ const auto resolvedName = [&](const QString name) -> QString {
++ // If isStrict we check validity already before.
++ const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
++ return isStrict
++ ? content
++ : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
++ };
++
++ const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
++ const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
++
++ return (invert ? u"!("_s : u"("_s) + result
++ + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
++ + u" == "_s
++ + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
++ + u"))"_s;
++ }
++
++ if ((m_typeResolver->isUnsignedInteger(rhsType)
++ && m_typeResolver->isUnsignedInteger(lhsType))
++ || (m_typeResolver->isSignedInteger(rhsType)
++ && m_typeResolver->isSignedInteger(lhsType))) {
++ // Both integers of same signedness: Let the C++ compiler perform the type promotion
++ return lhsName + sign + rhsName;
++ }
++
++ if (rhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(lhsType)) {
++ // Integral and bool: We can promote the bool to the integral type
++ return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
++ }
++
++ if (lhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(rhsType)) {
++ // Integral and bool: We can promote the bool to the integral type
++ return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
++ }
++
++ if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
++ // Both numbers: promote them to double
++ return convertStored(lhsType, m_typeResolver->realType(), lhsName)
++ + sign
++ + convertStored(rhsType, m_typeResolver->realType(), rhsName);
++ }
++
++ // If none of the above matches, we have to use QJSPrimitiveValue
++ return (invert ? u"!"_s : QString())
++ + convertStored(lhsType, primitive, lhsName)
++ + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
++ };
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
++ {
++ m_body += m_state.accumulatorVariableOut + u" = "_s;
++
++ const auto lhsType = registerType(lhs);
++ const QQmlJSScope::ConstPtr compareType =
++ m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
++ ? m_typeResolver->merge(lhsType.storedType(), m_state.accumulatorIn().storedType())
++ : m_typeResolver->jsPrimitiveType();
++
++ m_body += conversion(
++ m_typeResolver->boolType(), m_state.accumulatorOut(),
++ convertStored(registerType(lhs).storedType(), compareType,
++ consumedRegisterVariable(lhs))
++ + u' ' + cppOperator + u' '
++ + convertStored(m_state.accumulatorIn().storedType(), compareType,
++ consumedAccumulatorVariableIn()));
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
++ {
++ generateArithmeticOperation(
++ conversion(registerType(lhs), m_state.readRegister(lhs),
++ consumedRegisterVariable(lhs)),
++ conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
++ consumedAccumulatorVariableIn()),
++ cppOperator);
++ }
++
++ void QQmlJSCodeGenerator::generateShiftOperation(int lhs, const QString &cppOperator)
++ {
++ generateArithmeticOperation(
++ conversion(registerType(lhs), m_state.readRegister(lhs),
++ consumedRegisterVariable(lhs)),
++ u'(' + conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
++ consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
++ cppOperator);
++ }
++
++ void QQmlJSCodeGenerator::generateArithmeticOperation(
++ const QString &lhs, const QString &rhs, const QString &cppOperator)
++ {
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhs.isEmpty());
++ Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhs.isEmpty());
++
++ const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
++ m_body += m_state.accumulatorVariableOut;
++ m_body += u" = "_s;
++ const QString explicitCast
++ = originalOut.isStoredIn(m_typeResolver->stringType())
++ ? originalOut.storedType()->internalName()
++ : QString();
++ m_body += conversion(
++ originalOut, m_state.accumulatorOut(),
++ explicitCast + u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
++ m_body += u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
++ {
++ generateArithmeticOperation(
++ conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
++ consumedAccumulatorVariableIn()),
++ conversion(literalType(m_typeResolver->int32Type()),
++ m_state.readAccumulator(), QString::number(rhsConst)),
++ cppOperator);
++ }
++
++ void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
++ {
++ const auto var = conversion(m_state.accumulatorIn(),
++ originalType(m_state.readAccumulator()),
++ consumedAccumulatorVariableIn());
++
++ if (var == m_state.accumulatorVariableOut) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
++ return;
++ }
++
++ const auto originalResult = originalType(m_state.accumulatorOut());
++ if (m_state.accumulatorOut() == originalResult) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
++ m_body += m_state.accumulatorVariableOut + u" = "_s
++ + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
++ return;
++ }
++
++ m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
++ originalResult, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
++ {
++ {
++ // If actually in place, we cannot consume the variable.
++ const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
++ m_state.accumulatorVariableIn);
++ if (var == m_state.accumulatorVariableOut) {
++ m_body += cppOperator + var + u";\n"_s;
++ return;
++ }
++ }
++
++ const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
++ consumedAccumulatorVariableIn());
++
++ const auto originalResult = originalType(m_state.accumulatorOut());
++ if (m_state.accumulatorOut() == originalResult) {
++ m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
++ m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
++ return;
++ }
++
++ m_body += u"{\n"_s;
++ m_body += u"auto converted = "_s + var + u";\n"_s;
++ m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
++ originalResult, m_state.accumulatorOut(), u'('
++ + cppOperator + u"converted)"_s) + u";\n"_s;
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
++ const QString &resultPreparation)
++ {
++ m_body += u"#ifndef QT_NO_DEBUG\n"_s;
++ generateSetInstructionPointer();
++ m_body += u"#endif\n"_s;
++
++ if (!resultPreparation.isEmpty())
++ m_body += resultPreparation + u";\n"_s;
++ m_body += u"while (!"_s + lookup + u") {\n"_s;
++
++ m_body += u"#ifdef QT_NO_DEBUG\n"_s;
++ generateSetInstructionPointer();
++ m_body += u"#endif\n"_s;
++
++ m_body += initialization + u";\n"_s;
++ generateExceptionCheck();
++ if (!resultPreparation.isEmpty())
++ m_body += resultPreparation + u";\n"_s;
++ m_body += u"}\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset)
++ {
++ QString conversionCode;
++ const int absoluteOffset = nextInstructionOffset() + relativeOffset;
++ const auto annotation = m_annotations.find(absoluteOffset);
++ if (static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
++ const auto &conversions = annotation->second.typeConversions;
++
++ for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
++ regIt != regEnd; ++regIt) {
++ const QQmlJSRegisterContent targetType = regIt.value().content;
++ if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
++ continue;
++
++ const int registerIndex = regIt.key();
++ const auto variable = m_registerVariables.constFind(targetType);
++
++ if (variable == m_registerVariables.constEnd())
++ continue;
++
++ QQmlJSRegisterContent currentType;
++ QString currentVariable;
++ if (registerIndex == m_state.changedRegisterIndex()) {
++ currentVariable = changedRegisterVariable();
++ if (variable->variableName == currentVariable)
++ continue;
++
++ currentType = m_state.changedRegister();
++
++ // TODO: We can std::move the changed register in some cases here but it needs be
++ // done carefully (QTBUG-141920).
++ } else {
++ const auto it = m_state.registers.find(registerIndex);
++ if (it == m_state.registers.end()
++ || variable->variableName == registerVariable(registerIndex)) {
++ continue;
++ }
++
++ currentType = it.value().content;
++ currentVariable = consumedRegisterVariable(registerIndex);
++ }
++
++ // Actually == here. We want the jump code also for equal types
++ if (currentType == targetType)
++ continue;
++
++ conversionCode += variable->variableName;
++ conversionCode += u" = "_s;
++ conversionCode += conversion(currentType, targetType, currentVariable);
++ conversionCode += u";\n"_s;
++ }
++ }
++
++ if (relativeOffset) {
++ auto labelIt = m_labels.find(absoluteOffset);
++ if (labelIt == m_labels.end())
++ labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
++ conversionCode += u" goto "_s + *labelIt + u";\n"_s;
++ }
++
++ m_body += u"{\n"_s + conversionCode + u"}\n"_s;
++ }
++
++ QString QQmlJSCodeGenerator::registerVariable(int index) const
++ {
++ QQmlJSRegisterContent content = registerType(index);
++ const auto it = m_registerVariables.constFind(content);
++ if (it != m_registerVariables.constEnd())
++ return it->variableName;
++
++ return QString();
++ }
++
++ QString QQmlJSCodeGenerator::lookupVariable(int lookupIndex) const
++ {
++ for (auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
++ if (it.key().resultLookupIndex() == lookupIndex)
++ return it->variableName;
++ }
++ return QString();
++ }
++
++ QString QQmlJSCodeGenerator::consumedRegisterVariable(int index) const
++ {
++ const QString var = registerVariable(index);
++ if (var.isEmpty() || !shouldMoveRegister(index))
++ return var;
++ return u"std::move(" + var + u")";
++ }
++
++ QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn() const
++ {
++ return shouldMoveRegister(Accumulator)
++ ? u"std::move(" + m_state.accumulatorVariableIn + u")"
++ : m_state.accumulatorVariableIn;
++ }
++
++ QString QQmlJSCodeGenerator::changedRegisterVariable() const
++ {
++ QQmlJSRegisterContent changedRegister = m_state.changedRegister();
++
++ const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
++ if (storedType.isNull())
++ return QString();
++
++ return m_registerVariables.value(changedRegister).variableName;
++ }
++
++ QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
++ {
++ auto it = m_state.registers.find(index);
++ if (it != m_state.registers.end())
++ return it.value().content;
++
++ return QQmlJSRegisterContent();
++ }
++
++ QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(int lookupIndex) const
++ {
++ auto it = m_state.lookups.find(lookupIndex);
++ if (it != m_state.lookups.end())
++ return it.value().content;
++
++ return QQmlJSRegisterContent();
++ }
++
++ bool QQmlJSCodeGenerator::shouldMoveRegister(int index) const
++ {
++ return m_state.canMoveReadRegister(index)
++ && !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
++ }
++
++ QString QQmlJSCodeGenerator::conversion(
++ QQmlJSRegisterContent from, QQmlJSRegisterContent to, const QString &variable)
++ {
++ const QQmlJSScope::ConstPtr contained = to.containedType();
++
++ // If from is QJSPrimitiveValue and to contains a primitive we coerce using QJSPrimitiveValue
++ if (from.isStoredIn(m_typeResolver->jsPrimitiveType()) && m_typeResolver->isPrimitive(to)) {
++
++ QString primitive = [&]() -> QString {
++ if (contained == m_typeResolver->jsPrimitiveType())
++ return variable;
++
++ const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
++ if (contained == m_typeResolver->boolType())
++ return conversion.arg(u"Boolean"_s);
++ if (m_typeResolver->isIntegral(to))
++ return conversion.arg(u"Integer"_s);
++ if (m_typeResolver->isNumeric(to))
++ return conversion.arg(u"Double"_s);
++ if (contained == m_typeResolver->stringType())
++ return conversion.arg(u"String"_s);
++ REJECT<QString>(
++ u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
++ }();
++
++ if (primitive.isEmpty())
++ return primitive;
++
++ return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
++ }
++
++ if (to.isStoredIn(contained)
++ || m_typeResolver->isNumeric(to.storedType())
++ || to.storedType()->isReferenceType()
++ || from.contains(contained)) {
++ // If:
++ // * the output is not actually wrapped at all, or
++ // * the output is stored in a numeric type (as there are no internals to a number), or
++ // * the output is a QObject pointer, or
++ // * we merely wrap the value into a new container,
++ // we can convert by stored type.
++ return convertStored(from.storedType(), to.storedType(), variable);
++ } else {
++ return convertContained(from, to, variable);
++ }
++ }
++
++ QString QQmlJSCodeGenerator::convertStored(
++ const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
++ {
++ // TODO: most values can be moved, which is much more efficient with the common types.
++ // add a move(from, to, variable) function that implements the moves.
++ Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
++
++ const auto jsValueType = m_typeResolver->jsValueType();
++ const auto varType = m_typeResolver->varType();
++ const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
++ const auto boolType = m_typeResolver->boolType();
++
++ auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
++ if (to == boolType)
++ return u"false"_s;
++ if (m_typeResolver->isSignedInteger(to))
++ return u"0"_s;
++ if (m_typeResolver->isUnsignedInteger(to))
++ return u"0u"_s;
++ return QString();
++ };
++
++ if (from == m_typeResolver->voidType()) {
++ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
++ return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
++ const QString zero = zeroBoolOrInt(to);
++ if (!zero.isEmpty())
++ return zero;
++ if (to == m_typeResolver->floatType())
++ return u"std::numeric_limits<float>::quiet_NaN()"_s;
++ if (to == m_typeResolver->realType())
++ return u"std::numeric_limits<double>::quiet_NaN()"_s;
++ if (to == m_typeResolver->stringType())
++ return QQmlJSUtils::toLiteral(u"undefined"_s);
++ if (to == m_typeResolver->varType())
++ return u"QVariant()"_s;
++ if (to == m_typeResolver->jsValueType())
++ return u"QJSValue();"_s;
++ if (to == m_typeResolver->jsPrimitiveType())
++ return u"QJSPrimitiveValue()"_s;
++ if (from == to)
++ return QString();
++ }
++
++ if (from == m_typeResolver->nullType()) {
++ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
++ return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
++ if (to == jsValueType)
++ return u"QJSValue(QJSValue::NullValue)"_s;
++ if (to == jsPrimitiveType)
++ return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
++ if (to == varType)
++ return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
++ const QString zero = zeroBoolOrInt(to);
++ if (!zero.isEmpty())
++ return zero;
++ if (to == m_typeResolver->floatType())
++ return u"0.0f"_s;
++ if (to == m_typeResolver->realType())
++ return u"0.0"_s;
++ if (to == m_typeResolver->stringType())
++ return QQmlJSUtils::toLiteral(u"null"_s);
++ if (from == to)
++ return QString();
++ REJECT<QString>(u"Conversion from null to %1"_s.arg(to->internalName()));
++ }
++
++ if (from == to)
++ return variable;
++
++ if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
++ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
++ // Compare internalName here. The same C++ type can be exposed muliple times in
++ // different QML types. However, the C++ names have to be unique. We can always
++ // static_cast to those.
++
++ for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
++ // We still have to cast as other execution paths may result in different types.
++ if (base->internalName() == to->internalName())
++ return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
++ }
++ for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
++ if (base->internalName() == from->internalName())
++ return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
++ }
++ } else if (to == m_typeResolver->boolType()) {
++ return u'(' + variable + u" != nullptr)"_s;
++ }
++ }
++
++ auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
++ return candidate == jsValueType || candidate->isScript();
++ };
++
++ if (isJsValue(from) && isJsValue(to))
++ return variable;
++
++ const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
++ return m_typeResolver->isNumeric(type)
++ || type == m_typeResolver->boolType()
++ || type->scopeType() == QQmlSA::ScopeType::EnumScope;
++ };
++
++ if (from == m_typeResolver->realType() || from == m_typeResolver->floatType()) {
++ if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type()) {
++ return to->internalName() + u"(QJSNumberCoercion::roundTowards0("_s
++ + variable + u"))"_s;
++ }
++
++ if (m_typeResolver->isSignedInteger(to))
++ return u"QJSNumberCoercion::toInteger("_s + variable + u')';
++ if (m_typeResolver->isUnsignedInteger(to))
++ return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
++ if (to == m_typeResolver->boolType())
++ return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
++ }
++
++ if (isBoolOrNumber(from) && isBoolOrNumber(to))
++ return to->internalName() + u'(' + variable + u')';
++
++
++ if (from == jsPrimitiveType) {
++ if (to == boolType)
++ return variable + u".toBoolean()"_s;
++ if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type())
++ return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
++ if (m_typeResolver->isIntegral(to))
++ return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
++ if (m_typeResolver->isNumeric(to))
++ return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
++ if (to == m_typeResolver->stringType())
++ return variable + u".toString()"_s;
++ if (to == jsValueType)
++ return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
++ if (to == varType)
++ return variable + u".toVariant()"_s;
++ if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
++ return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
++ }
++
++ if (isJsValue(from)) {
++ if (to == jsPrimitiveType)
++ return variable + u".toPrimitive()"_s;
++ if (to == varType)
++ return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
++ return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
++ }
++
++ if (to == jsPrimitiveType) {
++ // null and undefined have been handled above already
++ Q_ASSERT(from != m_typeResolver->nullType());
++ Q_ASSERT(from != m_typeResolver->voidType());
++
++ if (from == m_typeResolver->boolType()
++ || from == m_typeResolver->int32Type()
++ || from == m_typeResolver->realType()
++ || from == m_typeResolver->stringType()) {
++ return u"QJSPrimitiveValue("_s + variable + u')';
++ } else if (from == m_typeResolver->int16Type()
++ || from == m_typeResolver->int8Type()
++ || from == m_typeResolver->uint16Type()
++ || from == m_typeResolver->uint8Type()) {
++ return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
++ } else if (m_typeResolver->isNumeric(from)) {
++ return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
++ }
++ }
++
++ if (to == jsValueType)
++ return u"aotContext->engine->toScriptValue("_s + variable + u')';
++
++ if (from == varType) {
++ if (to == m_typeResolver->listPropertyType())
++ return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
++ return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
++ + variable + u')';
++ }
++
++ if (to == varType)
++ return u"QVariant::fromValue("_s + variable + u')';
++
++ if (from == m_typeResolver->urlType() && to == m_typeResolver->stringType())
++ return variable + u".toString()"_s;
++
++ if (from == m_typeResolver->stringType() && to == m_typeResolver->urlType())
++ return u"QUrl("_s + variable + u')';
++
++ if (from == m_typeResolver->byteArrayType() && to == m_typeResolver->stringType())
++ return u"QString::fromUtf8("_s + variable + u')';
++
++ if (from == m_typeResolver->stringType() && to == m_typeResolver->byteArrayType())
++ return variable + u".toUtf8()"_s;
++
++ for (const auto &originType : {
++ m_typeResolver->dateTimeType(),
++ m_typeResolver->dateType(),
++ m_typeResolver->timeType()}) {
++ if (from == originType) {
++ for (const auto &targetType : {
++ m_typeResolver->dateTimeType(),
++ m_typeResolver->dateType(),
++ m_typeResolver->timeType(),
++ m_typeResolver->stringType(),
++ m_typeResolver->realType()}) {
++ if (to == targetType) {
++ return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
++ originType->internalName(), targetType->internalName(), variable);
++ }
++ }
++ break;
++ }
++ }
++
++ const auto retrieveFromPrimitive = [&](
++ const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
++ {
++ if (type == m_typeResolver->boolType())
++ return expression + u".toBoolean()"_s;
++ if (m_typeResolver->isSignedInteger(type))
++ return expression + u".toInteger()"_s;
++ if (m_typeResolver->isUnsignedInteger(type))
++ return u"uint("_s + expression + u".toInteger())"_s;
++ if (type == m_typeResolver->realType())
++ return expression + u".toDouble()"_s;
++ if (type == m_typeResolver->floatType())
++ return u"float("_s + expression + u".toDouble())"_s;
++ if (type == m_typeResolver->stringType())
++ return expression + u".toString()"_s;
++ return QString();
++ };
++
++ if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
++ const QString retrieve = retrieveFromPrimitive(
++ to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
++ if (!retrieve.isEmpty())
++ return retrieve;
++ }
++
++ if (from->isReferenceType() && to == m_typeResolver->stringType()) {
++ return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
++ + castTargetName(to) + u">("_s + variable + u')';
++ }
++
++ // Any value type is a non-null JS 'object' and therefore coerces to true.
++ if (to == m_typeResolver->boolType()) {
++ // All the interesting cases are already handled above:
++ Q_ASSERT(from != m_typeResolver->nullType());
++ Q_ASSERT(from != m_typeResolver->voidType());
++ Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
++ Q_ASSERT(!isBoolOrNumber(from));
++
++ return u"true"_s;
++ }
++
++ if (m_typeResolver->areEquivalentLists(from, to))
++ return variable;
++
++ if (from->isListProperty()
++ && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
++ && to->elementType()->isReferenceType()
++ && !to->isListProperty()) {
++ return variable + u".toList<"_s + to->internalName() + u">()"_s;
++ }
++
++ bool isExtension = false;
++ if (m_typeResolver->canPopulate(to, from, &isExtension)) {
++ REJECT<QString>(
++ u"populating "_s + to->internalName() + u" from "_s + from->internalName());
++ } else if (const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
++ ctor.isValid()) {
++ const auto argumentTypes = ctor.parameters();
++ const QString argument = convertStored(from, argumentTypes[0].type(), variable);
++ if (isExtension) {
++ // We typically use private inheritance for the foreign/extension trick.
++ // Therefore we need to jump through some hoops to extract the actual value here.
++ return u"[](auto &&arg) { "
++ "%1 wrapper(std::forward<decltype(arg)>(arg)); "
++ "return %2(reinterpret_cast<%2 &>(wrapper)); "
++ "}(%3)"_s.arg(
++ to->extensionType().scope->internalName(), to->internalName(), argument);
++ }
++
++ return u"%1(%2)"_s.arg(to->internalName(), argument);
++ }
++
++ if (to == m_typeResolver->stringType()
++ && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
++ addInclude(u"QtQml/qjslist.h"_s);
++
++ // Extend the life time of whatever variable is across the call to toString().
++ // variable may be an rvalue.
++ return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
++ + variable + u')';
++ }
++
++ // TODO: add more conversions
++
++ REJECT<QString>(
++ u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
++ }
++
++ QString QQmlJSCodeGenerator::convertContained(QQmlJSRegisterContent from, QQmlJSRegisterContent to, const QString &variable)
++ {
++ const QQmlJSScope::ConstPtr containedFrom = from.containedType();
++ const QQmlJSScope::ConstPtr containedTo = to.containedType();
++
++ // Those should be handled before, by convertStored().
++ Q_ASSERT(!to.storedType()->isReferenceType());
++ Q_ASSERT(!to.isStoredIn(containedTo));
++ Q_ASSERT(containedFrom != containedTo);
++
++ if (!to.isStoredIn(m_typeResolver->varType())
++ && !to.isStoredIn(m_typeResolver->jsPrimitiveType())) {
++ REJECT<QString>(u"internal conversion into unsupported wrapper type."_s);
++ }
++
++ bool isExtension = false;
++ if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
++ REJECT<QString>(u"populating "_s + containedTo->internalName()
++ + u" from "_s + containedFrom->internalName());
++ } else if (const auto ctor = m_typeResolver->selectConstructor(
++ containedTo, containedFrom, &isExtension); ctor.isValid()) {
++ return generateCallConstructor(
++ ctor, {from}, {variable}, metaType(containedTo),
++ metaObject(isExtension ? containedTo->extensionType().scope : containedTo));
++ }
++
++ const auto originalFrom = originalType(from);
++ const auto containedOriginalFrom = originalFrom.containedType();
++ if (containedFrom != containedOriginalFrom
++ && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
++ // If from is simply a wrapping of a specific type into a more general one, we can convert
++ // the original type instead. You can't nest wrappings after all.
++ return conversion(m_pool->storedIn(originalFrom, from.storedType()), to, variable);
++ }
++
++ if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
++ const QQmlJSRegisterContent intermediate
++ = m_pool->storedIn(from, m_typeResolver->jsPrimitiveType());
++ return conversion(intermediate, to, conversion(from, intermediate, variable));
++ }
++
++ REJECT<QString>(
++ u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
++ .arg(from.descriptiveName(), to.descriptiveName()));
++ }
++
++ void QQmlJSCodeGenerator::reject(const QString &thing)
++ {
++ addError(u"Cannot generate efficient code for %1"_s.arg(thing));
++ }
++
++
++ void QQmlJSCodeGenerator::skip(const QString &thing)
++ {
++ addSkip(u"Skipped code generation for %1"_s.arg(thing));
++ }
++
++ QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
++ : accumulatorOut(generator->m_state.accumulatorOut())
++ , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
++ , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
++ , generator(generator)
++ {
++ if (accumulatorVariableOut.isEmpty())
++ return;
++
++ const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
++ const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
++ const QQmlJSRegisterContent storage = accumulatorOut.storage();
++ const QQmlJSScope::ConstPtr stored = storage.containedType();
++ const QQmlJSScope::ConstPtr origStored = resolver->original(storage).containedType();
++
++
++ // If the stored type differs or if we store in QVariant and the contained type differs,
++ // then we have to use a temporary ...
++ if (origStored != stored
++ || (origContained != accumulatorOut.containedType() && stored == resolver->varType())) {
++
++ const bool storable = isTypeStorable(resolver, origStored);
++ generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
++ generator->m_state.setRegister(Accumulator, generator->originalType(accumulatorOut));
++ generator->m_body += u"{\n"_s;
++ if (storable) {
++ generator->m_body += origStored->augmentedInternalName() + u' '
++ + generator->m_state.accumulatorVariableOut + u";\n";
++ }
++ } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
++ && generator->m_state.readsRegister(Accumulator)
++ && generator->m_state.accumulatorOut().isStoredIn(resolver->varType())) {
++ // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
++ // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
++ // move the input out of the way first.
++ generator->m_state.accumulatorVariableIn
++ = generator->m_state.accumulatorVariableIn + u"_moved"_s;
++ generator->m_body += u"{\n"_s;
++ generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
++ + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
++ }
++ }
++
++ QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
++ {
++ if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
++ generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
++ generator->m_state.accumulatorOut(), accumulatorOut,
++ u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
++ generator->m_body += u"}\n"_s;
++ generator->m_state.setRegister(Accumulator, accumulatorOut);
++ generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
++ } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
++ generator->m_body += u"}\n"_s;
++ generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
++ }
++ }
++
++
++ QT_END_NAMESPACE
++
++ QQmlJSCodeGenerator::GeneratePragmaWarningBlock::GeneratePragmaWarningBlock(QQmlJSCodeGenerator *generator)
++ : m_generator(generator)
++ {
++ m_generator->m_body += u"QT_WARNING_PUSH\n"_s;
++ }
++
++ QQmlJSCodeGenerator::GeneratePragmaWarningBlock::~GeneratePragmaWarningBlock()
++ {
++ m_generator->m_body += u"QT_WARNING_POP\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::GeneratePragmaWarningBlock::silenceDivideByZero()
++ {
++ // currently only needed with MSVC, if we need it on more compilers, we
++ // should add a proper macro in Qt itself
++ m_generator->m_body += u"QT_WARNING_DISABLE_MSVC(4723) // potential divide by 0\n"_s;
++ }
++
++ void QQmlJSCodeGenerator::recordPropertyLookup(const QQmlJSScope::ConstPtr &base,
++ const QQmlJSMetaProperty &property)
++ {
++ if (!m_noAotValidation) {
++ if (!m_lookupSignaturesRecorder.recordPropertyLookup(base, property))
++ REJECT(m_lookupSignaturesRecorder.rejectMessage());
++ }
++ }
++
++ void QQmlJSCodeGenerator::recordMethodLookup(const QQmlJSScope::ConstPtr &base,
++ const QQmlJSMetaMethod &method)
++ {
++ if (!m_noAotValidation) {
++ if (!m_lookupSignaturesRecorder.recordMethodLookup(base, method))
++ REJECT(m_lookupSignaturesRecorder.rejectMessage());
++ }
++ }
++
++ void QQmlJSCodeGenerator::recordEnumKeyLookup(const QQmlJSScope::ConstPtr &base,
++ const QQmlJSMetaEnum &metaEnum,
++ const QString &keyName)
++ {
++ if (!m_noAotValidation) {
++ if (!m_lookupSignaturesRecorder.recordEnumKeyLookup(base, metaEnum, keyName))
++ REJECT(m_lookupSignaturesRecorder.rejectMessage());
++ }
++ }