/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qqmljscodegenerator_p.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE /*! * \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"// "_qs + QStringLiteral(#function) + u'\n'; \ } 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 QStringList &sourceCodeLines) : QQmlJSCompilePass(unitGenerator, typeResolver, logger) , m_sourceCodeLines(sourceCodeLines) , m_context(compilerContext) {} QString QQmlJSCodeGenerator::metaTypeFromType(const QQmlJSScope::ConstPtr &type) const { return u"QMetaType::fromType<"_qs + type->augmentedInternalName() + u">()"_qs; } QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type) const { return u"QMetaType::fromName(\""_qs + type->augmentedInternalName() + u"\")"_qs; } QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType) { if (!objectType->isComposite()) { if (objectType->internalName() == u"QObject"_qs || objectType->internalName() == u"QQmlComponent"_qs) { return u'&' + objectType->internalName() + u"::staticMetaObject"_qs; } return metaTypeFromName(objectType) + u".metaObject()"_qs; } reject(u"retrieving the metaObject of a composite type without using an instance."_qs); return QString(); } static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type) { return !type.isNull() && !resolver->equals(type, resolver->nullType()) && !resolver->equals(type, resolver->emptyListType()) && !resolver->equals(type, resolver->voidType()); } QQmlJSAotFunction QQmlJSCodeGenerator::run( const Function *function, const InstructionAnnotations *annotations, QQmlJS::DiagnosticMessage *error) { m_annotations = annotations; m_function = function; m_error = error; QHash> registerNames; auto addVariable = [&](int registerIndex, const QQmlJSScope::ConstPtr &seenType) { // Don't generate any variables for registers that are initialized with undefined. if (registerIndex == InvalidRegister || !isTypeStorable(m_typeResolver, seenType)) return; auto &typesForRegisters = m_registerVariables[registerIndex]; if (!typesForRegisters.contains(seenType)) { auto ¤tRegisterNames = registerNames[registerIndex]; QString &name = currentRegisterNames[m_typeResolver->comparableType(seenType)]; if (name.isEmpty()) name = u"r%1_%2"_qs.arg(registerIndex).arg(currentRegisterNames.count()); typesForRegisters[seenType] = name; } }; QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis") for (const auto &annotation : *m_annotations) { addVariable(annotation.second.changedRegisterIndex, annotation.second.changedRegister.storedType()); for (auto it = annotation.second.typeConversions.begin(), end = annotation.second.typeConversions.end(); it != end; ++it) { addVariable(it.key(), it.value().storedType()); } } QT_WARNING_POP // ensure we have m_labels for loops for (const auto loopLabel : m_context->labelInfo) m_labels.insert(loopLabel, u"label_%1"_qs.arg(m_labels.count())); m_state.State::operator=(initialState(function, m_typeResolver)); const QByteArray byteCode = function->code; decode(byteCode.constData(), static_cast(byteCode.length())); eliminateDeadStores(); QQmlJSAotFunction result; result.includes.swap(m_includes); QDuplicateTracker generatedVariables; for (const auto ®isterTypes : qAsConst(m_registerVariables)) { for (auto registerTypeIt = registerTypes.constBegin(), end = registerTypes.constEnd(); registerTypeIt != end; ++registerTypeIt) { const QQmlJSScope::ConstPtr storedType = registerTypeIt.key(); if (generatedVariables.hasSeen(registerTypeIt.value())) continue; result.code += storedType->internalName(); if (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) result.code += u" *"_qs; else result.code += u' '; result.code += registerTypeIt.value(); result.code += u";\n"_qs; } } for (const Section §ion : m_sections) result.code += section.code(); for (const QQmlJSScope::ConstPtr &argType : qAsConst(function->argumentTypes)) { if (argType) { result.argumentTypes.append(argType->augmentedInternalName()); } else { result.argumentTypes.append(u"void"_qs); } } if (function->returnType) { result.returnType = function->returnType->internalName(); if (function->returnType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) result.returnType += u'*'; } else { result.returnType = u"void"_qs; } return result; } QList QQmlJSCodeGenerator::findBasicBlocks( const QList §ions) { using JumpMode = QQmlJSCodeGenerator::JumpMode; QList basicBlocks; BasicBlock currentBlock; currentBlock.beginSection = 0; for (int i = 0, end = sections.length(); i != end; ++i) { const QQmlJSCodeGenerator::Section §ion = sections[i]; const QString label = section.label(); if (!label.isEmpty() || currentBlock.jumpMode != JumpMode::None) { if (currentBlock.beginSection != i) { currentBlock.endSection = i; basicBlocks.append(currentBlock); currentBlock.beginSection = i; } currentBlock.label = label; } currentBlock.jumpMode = section.jumpMode(); currentBlock.jumpTarget = section.jumpTarget(); } currentBlock.endSection = sections.length(); basicBlocks.append(currentBlock); BasicBlock *prevBlock = nullptr; for (int i = 0, end = basicBlocks.length(); i != end; ++i) { BasicBlock *block = &basicBlocks[i]; if (prevBlock && prevBlock->jumpMode != JumpMode::Unconditional) block->previousBlocks.append(i); if (!block->label.isEmpty()) { for (int j = 0, end = basicBlocks.length(); j != end; ++j) { BasicBlock *jumpFrom = &basicBlocks[j]; if (jumpFrom->jumpMode == JumpMode::None || jumpFrom->jumpTarget != block->label) continue; jumpFrom->jumpTargetBlock = i; block->previousBlocks.append(j); } } } return basicBlocks; } QQmlJSCodeGenerator::RequiredRegisters QQmlJSCodeGenerator::dropPreserveCycles( const QList &basicBlocks, const QQmlJSCodeGenerator::RequiredRegisters &requiredRegisters) { RequiredRegisters result(requiredRegisters.length()); for (int i = 0, blocksEnd = basicBlocks.length(); i != blocksEnd; ++i) { const BasicBlock &block = basicBlocks[i]; const QHash ®isters = requiredRegisters[i]; QHash &resultRegisters = result[i]; for (auto r = registers.begin(), registersEnd = registers.end(); r != registersEnd; ++r) { const QString variable = r.key(); switch (*r) { case ReadMode::NoRead: resultRegisters[variable] = ReadMode::NoRead; continue; case ReadMode::Preserve: // Convert Preserve into NoRead and update below if (!resultRegisters.contains(variable)) resultRegisters[variable] = ReadMode::NoRead; continue; case ReadMode::SelfRead: resultRegisters[variable] = ReadMode::SelfRead; break; } QList blocksToCheck; blocksToCheck.append(block.previousBlocks); while (!blocksToCheck.isEmpty()) { const int currentIndex = blocksToCheck.takeFirst(); if (requiredRegisters[currentIndex][variable] == ReadMode::Preserve) { result[currentIndex][variable] = ReadMode::Preserve; blocksToCheck.append(basicBlocks[currentIndex].previousBlocks); } } } } return result; } void QQmlJSCodeGenerator::eliminateDeadStores() { const QList basicBlocks = findBasicBlocks(m_sections); QList toErase; bool foundUnknownBlock = false; RequiredRegisters requiredRegisters(basicBlocks.length()); do { toErase.clear(); foundUnknownBlock = false; for (auto ®isterTypes : m_registerVariables) { for (auto registerTypeIt = registerTypes.constBegin(); registerTypeIt != registerTypes.constEnd();) { const QString variable = registerTypeIt.value(); // Don't declare any variables for registers that contain undefined. if (variable.isEmpty()) { registerTypeIt = registerTypes.erase(registerTypeIt); continue; } bool usedOnce = false; ReadMode inUse = ReadMode::NoRead; int basicBlockIndex = basicBlocks.length() - 1; for (int i = m_sections.length() - 1; i >= 0; --i) { Section §ion = m_sections[i]; const BasicBlock *block = &basicBlocks[basicBlockIndex]; if (block->beginSection > i) { requiredRegisters[basicBlockIndex][variable] = inUse; block = &basicBlocks[--basicBlockIndex]; if (block->jumpMode == JumpMode::Unconditional) inUse = ReadMode::NoRead; else if (inUse == ReadMode::SelfRead) inUse = ReadMode::Preserve; if (block->jumpMode != JumpMode::None) { QHash *blockRegisters = &requiredRegisters[block->jumpTargetBlock]; auto req = blockRegisters->find(variable); if (req == blockRegisters->end()) { foundUnknownBlock = true; inUse = ReadMode::Preserve; } else if (*req != ReadMode::NoRead) { inUse = ReadMode::Preserve; } } } if (section.writeRegister() == variable) { if (inUse == ReadMode::NoRead && !section.hasSideEffects()) { toErase.append(i); } else { usedOnce = true; // We can read and write the same register in one instruction. // See Increment and Decrement inUse = section.readsRegister(variable) ? ReadMode::SelfRead : ReadMode::NoRead; } } else if (section.readsRegister(variable)) { inUse = ReadMode::SelfRead; usedOnce = true; } } requiredRegisters[0][variable] = inUse; if (!usedOnce) { registerTypeIt = registerTypes.erase(registerTypeIt); continue; } ++registerTypeIt; } } // Sort the offsets in reverse order so that we can pop from the back std::sort(toErase.begin(), toErase.end()); auto end = std::unique(toErase.begin(), toErase.end()); int eraseIndex = (end - toErase.begin()) - 1; for (int i = m_sections.length() - 1; i >= 0 && eraseIndex >= 0; --i) { if (i != toErase[eraseIndex]) continue; Section §ion = m_sections[i]; QString code = section.code(); section = Section(); // Comment out the section. code.replace(u'\n', u"\n// "_qs); if (!code.startsWith(u"// "_qs) && !code.startsWith(u'\n')) code.prepend(u"// "_qs); // Make sure we end with a newline so that we don't comment out the next section. if (code.endsWith(u"\n// ")) code.chop(3); else if (!code.endsWith(u'\n')) code.append(u'\n'); section += code; --eraseIndex; } if (foundUnknownBlock) requiredRegisters = dropPreserveCycles(basicBlocks, requiredRegisters); } while (!toErase.isEmpty() || foundUnknownBlock); } QString QQmlJSCodeGenerator::errorReturnValue() { if (m_function->returnType) return conversion(m_typeResolver->voidType(), m_function->returnType, QString()); return QString(); } void QQmlJSCodeGenerator::generate_Ret() { INJECT_TRACE_INFO(generate_Ret); m_body.setWriteRegister(QString()); m_body.setHasSideEffects(true); if (m_function->returnType) { const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs; if (!m_state.accumulatorVariableIn.isEmpty()) { const QString in = use(m_state.accumulatorVariableIn); if (m_typeResolver->registerIsStoredIn( m_state.accumulatorIn(), m_typeResolver->varType())) { m_body += u"if (!"_qs + in + u".isValid())\n"_qs; m_body += u" "_qs + signalUndefined; } else if (m_typeResolver->registerIsStoredIn( m_state.accumulatorIn(), m_typeResolver->jsPrimitiveType())) { m_body += u"if ("_qs + in + u".type() == QJSPrimitiveValue::Undefined)\n"_qs; m_body += u" "_qs + signalUndefined; } else if (m_typeResolver->registerIsStoredIn( m_state.accumulatorIn(), m_typeResolver->jsValueType())) { m_body += u"if ("_qs + in + u".isUndefined())\n"_qs; m_body += u" "_qs + signalUndefined; } m_body += u"return "_qs + conversion(m_state.accumulatorIn().storedType(), m_function->returnType, in); } else { if (m_typeResolver->equals(m_state.accumulatorIn().storedType(), m_typeResolver->voidType())) { m_body += signalUndefined; } m_body += u"return "_qs + conversion( m_state.accumulatorIn().storedType(), m_function->returnType, QString()); } } else { m_body += u"return"_qs; } m_body += u";\n"_qs; m_skipUntilNextLabel = true; } void QQmlJSCodeGenerator::generate_Debug() { BYTECODE_UNIMPLEMENTED(); } static QString toNumericString(double value) { if (value >= std::numeric_limits::min() && value <= std::numeric_limits::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::infinity()"_qs; return std::signbit(value) ? (u'-' + inf) : inf; } case FP_NAN: return u"std::numeric_limits::quiet_NaN()"_qs; case FP_ZERO: return std::signbit(value) ? u"-0.0"_qs : u"0"_qs; default: break; } return QString::number(value, 'f', std::numeric_limits::max_digits10); } void QQmlJSCodeGenerator::generate_LoadConst(int index) { INJECT_TRACE_INFO(generate_LoadConst); auto encodedConst = m_jsUnitGenerator->constant(index); double value = QV4::StaticValue::fromReturnedValue(encodedConst).doubleValue(); m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; m_body += toNumericString(value); // ### handle other types m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadZero() { INJECT_TRACE_INFO(generate_LoadZero); m_body += m_state.accumulatorVariableOut; m_body += u" = 0"_qs; m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadTrue() { INJECT_TRACE_INFO(generate_LoadTrue); m_body += m_state.accumulatorVariableOut; m_body += u" = true"_qs; m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadFalse() { INJECT_TRACE_INFO(generate_LoadFalse); m_body += m_state.accumulatorVariableOut; m_body += u" = false"_qs; m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadNull() { INJECT_TRACE_INFO(generate_LoadNull); // No need to generate code. We don't store std::nullptr_t. if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), m_typeResolver->nullType())) return; m_body += m_state.accumulatorVariableOut + u" = "_qs; m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut().storedType(), u"nullptr"_qs); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadUndefined() { INJECT_TRACE_INFO(generate_LoadUndefined); // No need to generate code. We don't store void. if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), m_typeResolver->voidType())) return; m_body += m_state.accumulatorVariableOut + u" = "_qs; m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(), QString()); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadInt(int value) { INJECT_TRACE_INFO(generate_LoadInt); m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(), QString::number(value)); m_body += u";\n"_qs; } 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' m_body.setWriteRegister(var); const auto v4Value = QV4::StaticValue::fromReturnedValue( m_jsUnitGenerator->constant(constIndex)); const auto changed = m_state.changedRegister().storedType(); m_body += var + u" = "_qs; if (v4Value.isNull()) { m_body += conversion(m_typeResolver->nullType(), changed, QString()); } else if (v4Value.isUndefined()) { m_body += conversion(m_typeResolver->voidType(), changed, QString()); } else if (v4Value.isBoolean()) { m_body += conversion(m_typeResolver->boolType(), changed, v4Value.booleanValue() ? u"true"_qs : u"false"_qs); } else if (v4Value.isInteger()) { m_body += conversion(m_typeResolver->intType(), changed, QString::number(v4Value.int_32())); } else if (v4Value.isDouble()) { m_body += conversion(m_typeResolver->realType(), changed, toNumericString(v4Value.doubleValue())); } else { reject(u"unknown const type"_qs); } m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadReg(int reg) { INJECT_TRACE_INFO(generate_LoadReg); // We won't emit any code for loading undefined. // See also generate_LoadUndefined() if (m_typeResolver->registerContains(m_state.accumulatorOut(), m_typeResolver->voidType())) return; m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg))); m_body += u";\n"_qs; } 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(); m_body.setWriteRegister(var); if (var.isEmpty()) return; // don't store "undefined" m_body += var; m_body += u" = "_qs; m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(), use(m_state.accumulatorVariableIn)); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg) { INJECT_TRACE_INFO(generate_MoveReg); Q_ASSERT(m_state.changedRegisterIndex() == destReg); const QString destRegName = changedRegisterVariable(); m_body.setWriteRegister(destRegName); if (destRegName.isEmpty()) return; // don't store things we cannot store. m_body += destRegName; m_body += u" = "_qs; m_body += conversion(registerType(srcReg), m_state.changedRegister(), use(registerVariable(srcReg))); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_LoadImport(int index) { Q_UNUSED(index) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_LoadLocal(int index) { Q_UNUSED(index); reject(u"LoadLocal"_qs); } 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" = "_qs; m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(), QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId))); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg) { Q_UNUSED(regExpId) Q_UNUSED(destReg) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_LoadClosure(int value) { Q_UNUSED(value) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_LoadName(int nameIndex) { Q_UNUSED(nameIndex) reject(u"LoadName"_qs); } void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index) { INJECT_TRACE_INFO(generate_LoadGlobalLookup); const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index) + u", &"_qs + m_state.accumulatorVariableOut + u", "_qs + metaTypeFromType(m_state.accumulatorOut().storedType()) + u')'; const QString initialization = u"aotContext->initLoadGlobalLookup("_qs + QString::number(index) + u')'; generateLookup(lookup, initialization); } void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index) { INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup); if (m_state.accumulatorVariableOut.isEmpty()) return; AccumulatorConverter registers(this); const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index); const QString name = m_jsUnitGenerator->stringForIndex(nameIndex); if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) { m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion( m_typeResolver->jsValueType(), m_state.accumulatorOut().storedType(), u"aotContext->javaScriptGlobalProperty("_qs + QString::number(nameIndex) + u")") + u";\n"_qs; return; } const QString indexString = QString::number(index); if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) { const QString lookup = u"aotContext->loadContextIdLookup("_qs + indexString + u", "_qs + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')'; const QString initialization = u"aotContext->initLoadContextIdLookup("_qs + indexString + u')'; generateLookup(lookup, initialization); return; } const bool isProperty = m_state.accumulatorOut().isProperty(); const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType(); const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType(); if (isProperty) { const auto lookupType = contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut); const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_qs + indexString + u", "_qs + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')'; const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_qs + indexString + u", "_qs + lookupType + u')'; const QString preparation = getLookupPreparation( m_state.accumulatorOut(), m_state.accumulatorVariableOut, index); generateLookup(lookup, initialization, preparation); } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) { generateTypeLookup(index); } else { reject(u"lookup of %1"_qs.arg(m_state.accumulatorOut().descriptiveName())); } } void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex) { INJECT_TRACE_INFO(generate_StoreNameSloppy); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); const QString name = m_jsUnitGenerator->stringForIndex(nameIndex); const QQmlJSRegisterContent specific = m_typeResolver->scopedType(m_function->qmlScope, name); const QQmlJSRegisterContent type = specific.storedIn( m_typeResolver->genericType(specific.storedType())); Q_ASSERT(type.isProperty()); switch (type.variant()) { case QQmlJSRegisterContent::ScopeProperty: case QQmlJSRegisterContent::ExtensionScopeProperty: { if (!m_typeResolver->registerContains(m_state.accumulatorIn(), type.property().type())) { m_body += u"{\n"_qs; m_body += u"auto converted = "_qs + conversion(m_state.accumulatorIn(), type, use(m_state.accumulatorVariableIn)) + u";\n"_qs; m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex) + u", "_qs + contentPointer(type, u"converted"_qs) + u", "_qs + contentType(type, u"converted"_qs) + u')'; m_body += u";\n"_qs; m_body += u"}\n"_qs; } else { m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex) + u", "_qs + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u", "_qs + contentType(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u')'; m_body += u";\n"_qs; } break; } case QQmlJSRegisterContent::ScopeMethod: case QQmlJSRegisterContent::ExtensionScopeMethod: reject(u"assignment to scope method"_qs); break; 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 (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) { reject(u"LoadElement with non-list base type or non-numeric arguments"_qs); return; } if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) { reject(u"indirect LoadElement"_qs); return; } const QString baseName = use(registerVariable(base)); const QString indexName = use(m_state.accumulatorVariableIn); // Our QQmlListProperty only keeps plain QObject*. const auto valueType = m_typeResolver->valueType(baseType); const auto elementType = m_typeResolver->globalType( m_typeResolver->genericType(m_typeResolver->containedType(valueType))); m_body += u"if ("_qs + indexName + u" >= 0 && "_qs + indexName + u" < "_qs + baseName + u".count(&"_qs + baseName + u"))\n"_qs; m_body += u" "_qs + m_state.accumulatorVariableOut + u" = "_qs + conversion(elementType, m_state.accumulatorOut(), baseName + u".at(&"_qs + baseName + u", "_qs + indexName + u')') + u";\n"_qs; m_body += u"else\n"_qs; m_body += u" "_qs + m_state.accumulatorVariableOut + u" = {}"_qs; m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_StoreElement(int base, int index) { INJECT_TRACE_INFO(generate_StoreElement); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); const QQmlJSRegisterContent baseType = registerType(base); if (!m_typeResolver->isNumeric(registerType(index)) || !baseType.isList()) { reject(u"StoreElement with non-list base type or non-numeric arguments"_qs); return; } if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) { reject(u"indirect StoreElement"_qs); return; } const QString baseName = use(registerVariable(base)); const QString indexName = use(registerVariable(index)); const auto valueType = m_typeResolver->valueType(baseType); const auto elementType = m_typeResolver->globalType(m_typeResolver->genericType( m_typeResolver->containedType(valueType))); m_body += u"if ("_qs + indexName + u" >= 0 && "_qs + indexName + u" < "_qs + baseName + u".count(&"_qs + baseName + u"))\n"_qs; m_body += u" "_qs + baseName + u".replace(&"_qs + baseName + u", "_qs + indexName + u", "_qs; m_body += conversion(m_state.accumulatorIn(), elementType, use(m_state.accumulatorVariableIn)) + u");\n"_qs; } void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex) { Q_UNUSED(nameIndex) reject(u"LoadProperty"_qs); } 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. if (enumMember.isEmpty()) return; // 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" = "_qs + QString::number(metaEnum.value(enumMember)); m_body += u";\n"_qs; 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(); const QString lookup = u"aotContext->getEnumLookup("_qs + QString::number(index) + u", &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initGetEnumLookup("_qs + QString::number(index) + u", "_qs + metaObject(scopeType) + u", \""_qs + enumName + u"\", \""_qs + enumMember + u"\")"_qs; generateLookup(lookup, initialization); } void QQmlJSCodeGenerator::generateTypeLookup(int index) { const QString indexString = QString::number(index); const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator); const QString namespaceString = accumulatorIn.isImportNamespace() ? QString::number(accumulatorIn.importNamespace()) : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs; switch (m_state.accumulatorOut().variant()) { case QQmlJSRegisterContent::Singleton: { const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString + u", &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadSingletonLookup("_qs + indexString + u", "_qs + namespaceString + u')'; generateLookup(lookup, initialization); break; } case QQmlJSRegisterContent::ScopeModulePrefix: break; case QQmlJSRegisterContent::ScopeAttached: { const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString + u", aotContext->qmlScopeObject, &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadAttachedLookup("_qs + indexString + u", "_qs + namespaceString + u", aotContext->qmlScopeObject)"_qs; generateLookup(lookup, initialization); break; } case QQmlJSRegisterContent::Script: reject(u"script lookup"_qs); break; case QQmlJSRegisterContent::MetaType: { const QString lookup = u"aotContext->loadTypeLookup("_qs + indexString + u", &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadTypeLookup("_qs + indexString + u", "_qs + namespaceString + u")"_qs; generateLookup(lookup, initialization); break; } default: Q_UNREACHABLE(); } } void QQmlJSCodeGenerator::generate_GetLookup(int index) { INJECT_TRACE_INFO(generate_GetLookup); if (m_state.accumulatorOut().isMethod()) { reject(u"lookup of function property."_qs); return; } if (m_state.accumulatorOut().isImportNamespace()) { Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix); // 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" = "_qs + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(), use(m_state.accumulatorVariableIn)) + u";\n"_qs; } else { m_body.setWriteRegister(QString()); } 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"_qs; const auto accumulatorIn = m_state.accumulatorIn(); const bool isReferenceType = (accumulatorIn.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Reference); switch (m_state.accumulatorOut().variant()) { case QQmlJSRegisterContent::ScopeAttached: { const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString + u", aotContext->qmlScopeObject, &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadAttachedLookup("_qs + indexString + u", "_qs + namespaceString + u", aotContext->qmlScopeObject)"_qs; generateLookup(lookup, initialization); return; } case QQmlJSRegisterContent::ObjectAttached: { 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"_qs); } const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString + u", "_qs + use(m_state.accumulatorVariableIn) + u", &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadAttachedLookup("_qs + indexString + u", "_qs + namespaceString + u", "_qs + use(m_state.accumulatorVariableIn) + u')'; generateLookup(lookup, initialization); return; } case QQmlJSRegisterContent::Singleton: { const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString + u", &"_qs + m_state.accumulatorVariableOut + u')'; const QString initialization = u"aotContext->initLoadSingletonLookup("_qs + indexString + u", "_qs + namespaceString + u')'; generateLookup(lookup, initialization); return; } default: break; } Q_ASSERT(m_state.accumulatorOut().isProperty()); const QQmlJSScope::ConstPtr out = m_state.accumulatorOut().storedType(); if (isReferenceType) { const QString lookup = u"aotContext->getObjectLookup("_qs + indexString + u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')'; const QString initialization = u"aotContext->initGetObjectLookup("_qs + indexString + u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')'; const QString preparation = getLookupPreparation( m_state.accumulatorOut(), m_state.accumulatorVariableOut, index); generateLookup(lookup, initialization, preparation); } else if ((m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->stringType()) || accumulatorIn.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) && m_jsUnitGenerator->lookupName(index) == u"length"_qs) { // Special-cased the same way as in QQmlJSTypeResolver::memberType() m_body += m_state.accumulatorVariableOut + u" = "_qs + use(m_state.accumulatorVariableIn) + u".count("_qs; if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType())) m_body += u'&' + use(m_state.accumulatorVariableIn); m_body += u')' + u";\n"_qs; } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) { reject(u"lookup in QJSValue"_qs); } else { const QString lookup = u"aotContext->getValueLookup("_qs + indexString + u", "_qs + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u", "_qs + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')'; const QString initialization = u"aotContext->initGetValueLookup("_qs + indexString + u", "_qs + metaObject(m_state.accumulatorOut().scopeType()) + u", "_qs + 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_GetOptionalLookup(int index, int offset) { Q_UNUSED(index) Q_UNUSED(offset) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg) { Q_UNUSED(nameIndex) Q_UNUSED(baseReg) reject(u"StoreProperty"_qs); } QString QQmlJSCodeGenerator::setLookupPreparation( const QQmlJSRegisterContent &content, const QString &arg, int lookup) { if (m_typeResolver->registerContains(content, content.storedType())) return QString(); if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) { return u"const QMetaType argType = aotContext->lookupResultMetaType("_qs + QString::number(lookup) + u");\n"_qs + u"if (argType.isValid())\n "_qs + arg + u".convert(argType)"; } // TODO: We could make sure they're compatible, for example QObject pointers. return QString(); } void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg) { INJECT_TRACE_INFO(generate_SetLookup); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); const QString indexString = QString::number(index); const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType(); const QQmlJSRegisterContent callBase = registerType(baseReg); const QQmlJSRegisterContent specific = m_typeResolver->memberType( callBase, m_jsUnitGenerator->lookupName(index)); const QQmlJSRegisterContent property = specific.storedIn( m_typeResolver->genericType(specific.storedType())); const QString object = use(registerVariable(baseReg)); m_body += u"{\n"_qs; QString variableIn; QString variableInType; QString preparation; QString argType; if (!m_typeResolver->registerContains( m_state.accumulatorIn(), m_typeResolver->containedType(property))) { m_body += u"auto converted = "_qs + conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn)) + u";\n"_qs; variableIn = contentPointer(property, u"converted"_qs); variableInType = contentType(property, u"converted"_qs); preparation = setLookupPreparation(property, u"converted"_qs, index); if (preparation.isEmpty()) argType = contentType(property, u"converted"_qs); else argType = u"argType"_qs; } else { variableIn = contentPointer(property, use(m_state.accumulatorVariableIn)); variableInType = contentType(property, use(m_state.accumulatorVariableIn)); argType = variableInType; } switch (callBase.storedType()->accessSemantics()) { case QQmlJSScope::AccessSemantics::Reference: { const QString lookup = u"aotContext->setObjectLookup("_qs + indexString + u", "_qs + object + u", "_qs + variableIn + u')'; const QString initialization = u"aotContext->initSetObjectLookup("_qs + indexString + u", "_qs + object + u", "_qs + argType + u')'; generateLookup(lookup, initialization, preparation); break; } case QQmlJSScope::AccessSemantics::Sequence: { const QString propertyName = m_jsUnitGenerator->lookupName(index); if (propertyName != u"length"_qs) { reject(u"setting non-length property on a sequence type"_qs); break; } if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) { reject(u"SetLookup on sequence types (because of missing write-back)"_qs); break; } // We can resize without write back on a list property because it's actually a reference. m_body += u"const int begin = "_qs + object + u".count(&" + object + u");\n"_qs; m_body += u"const int end = "_qs + (variableIn.startsWith(u'&') ? variableIn.mid(1) : (u'*' + variableIn)) + u";\n"_qs; m_body += u"for (int i = begin; i < end; ++i)\n"_qs; m_body += u" "_qs + object + u".append(&"_qs + object + u", nullptr);\n"_qs; m_body += u"for (int i = begin; i > end; --i)\n"_qs; m_body += u" "_qs + object + u".removeLast(&"_qs + object + u')' + u";\n"_qs; break; } case QQmlJSScope::AccessSemantics::Value: { const QString propertyName = m_jsUnitGenerator->lookupName(index); const QQmlJSRegisterContent specific = m_typeResolver->memberType(callBase, propertyName); Q_ASSERT(specific.isProperty()); const QQmlJSRegisterContent property = specific.storedIn( m_typeResolver->genericType(specific.storedType())); const QString lookup = u"aotContext->setValueLookup("_qs + indexString + u", "_qs + contentPointer(registerType(baseReg), object) + u", "_qs + variableIn + u')'; const QString initialization = u"aotContext->initSetValueLookup("_qs + indexString + u", "_qs + metaObject(property.scopeType()) + u", "_qs + contentType(registerType(baseReg), object) + u')'; generateLookup(lookup, initialization, preparation); reject(u"SetLookup on value types (because of missing write-back)"_qs); break; } case QQmlJSScope::AccessSemantics::None: Q_UNREACHABLE(); break; } m_body += u"}\n"_qs; } 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::argumentsList(int argc, int argv, QString *outVar) { QString types; QString args; QString conversions; if (m_state.changedRegisterIndex() == InvalidRegister || m_typeResolver->registerContains( m_state.accumulatorOut(), m_typeResolver->voidType())) { types = u"QMetaType()"_qs; args = u"nullptr"_qs; } else { *outVar = u"callResult"_qs; const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType(); m_body += outType->internalName(); if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) m_body += u" *"_qs; else m_body += u' '; m_body += *outVar + u";\n"; types = metaTypeFromType(m_state.accumulatorOut().storedType()); args = u'&' + *outVar; } for (int i = 0; i < argc; ++i) { const QQmlJSRegisterContent content = registerType(argv + i); const QString var = use(registerVariable(argv + i)); if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) { QString argName = u"arg"_qs + QString::number(i); conversions += u"QVariant "_qs + argName + u" = "_qs + conversion(content.storedType(), m_typeResolver->varType(), var) + u";\n"_qs; args += u", "_qs + argName + u".data()"_qs; types += u", "_qs + argName + u".metaType()"_qs; } else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) { args += u", "_qs + var + u".data()"_qs; types += u", "_qs + var + u".metaType()"_qs; } else { args += u", &"_qs + var; types += u", "_qs + metaTypeFromType(content.storedType()); } } return conversions + u"void *args[] = { "_qs + args + u" };\n"_qs + u"const QMetaType types[] = { "_qs + types + u" };\n"_qs; } void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar) { if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty()) return; nextSection(); m_body.setWriteRegister(m_state.accumulatorVariableOut); m_body += m_state.accumulatorVariableOut + u" = "_qs; m_body += u"std::move(" + outVar + u");\n"; nextSection(); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); } 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"_qs); } bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv) { addInclude(u"cmath"_qs); addInclude(u"limits"_qs); addInclude(u"qalgorithms.h"_qs); addInclude(u"qrandom.h"_qs); // 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"_qs; for (int i = 0; i < argc; ++i) { m_body += u"const double arg%1 = "_qs.arg(i + 1) + conversion( registerType(argv + i).storedType(), m_typeResolver->realType(), use(registerVariable(argv + i))) + u";\n"_qs; } const QString qNaN = u"std::numeric_limits::quiet_NaN()"_qs; const QString inf = u"std::numeric_limits::infinity()"_qs; m_body += m_state.accumulatorVariableOut + u" = "_qs; QString expression; if (name == u"abs" && argc == 1) { expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs; } else if (name == u"acos"_qs && argc == 1) { expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN); } else if (name == u"acosh"_qs && argc == 1) { expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN); } else if (name == u"asin"_qs && argc == 1) { expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN); } else if (name == u"asinh"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs; } else if (name == u"atan"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs; } else if (name == u"atanh"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs; } else if (name == u"atan2"_qs) { // TODO: complicated return false; } else if (name == u"cbrt"_qs && argc == 1) { expression = u"std::cbrt(arg1)"_qs; } else if (name == u"ceil"_qs && argc == 1) { expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs; } else if (name == u"clz32"_qs && argc == 1) { expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs; } else if (name == u"cos"_qs && argc == 1) { expression = u"std::cos(arg1)"_qs; } else if (name == u"cosh"_qs && argc == 1) { expression = u"std::cosh(arg1)"_qs; } else if (name == u"exp"_qs && argc == 1) { expression = u"std::isinf(arg1) " "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) " ": std::exp(arg1)"_qs.arg(inf); } else if (name == u"expm1"_qs) { // TODO: complicated return false; } else if (name == u"floor"_qs && argc == 1) { expression = u"std::floor(arg1)"_qs; } else if (name == u"fround"_qs && argc == 1) { expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) " "? arg1 " ": double(float(arg1))"_qs; } else if (name == u"hypot"_qs) { // TODO: complicated return false; } else if (name == u"imul"_qs && argc == 2) { expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) " "* quint32(QJSNumberCoercion::toInteger(arg2)))"_qs; } else if (name == u"log"_qs && argc == 1) { expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN); } else if (name == u"log10"_qs && argc == 1) { expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN); } else if (name == u"log1p"_qs && argc == 1) { expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN); } else if (name == u"log2"_qs && argc == 1) { expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN); } else if (name == u"max"_qs && argc == 2) { expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) " "? arg2 " ": ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs; } else if (name == u"min"_qs && argc == 2) { expression = u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) " "? arg2 " ": ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1)"_qs; } else if (name == u"pow"_qs) { // TODO: complicated return false; } else if (name == u"random"_qs && argc == 0) { expression = u"QRandomGenerator::global()->generateDouble()"_qs; } else if (name == u"round"_qs && argc == 1) { expression = u"std::isfinite(arg1) " "? ((arg1 < 0.5 && arg1 >= -0.5) " "? std::copysign(0.0, arg1) " ": std::floor(arg1 + 0.5)) " ": arg1"_qs; } else if (name == u"sign"_qs && argc == 1) { expression = u"std::isnan(arg1) " "? %1 " ": (qIsNull(arg1) " "? arg1 " ": (std::signbit(arg1) ? -1.0 : 1.0))"_qs.arg(qNaN); } else if (name == u"sin"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs; } else if (name == u"sinh"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs; } else if (name == u"sqrt"_qs && argc == 1) { expression = u"std::sqrt(arg1)"_qs; } else if (name == u"tan"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs; } else if (name == u"tanh"_qs && argc == 1) { expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs; } else if (name == u"trunc"_qs && argc == 1) { expression = u"std::trunc(arg1)"_qs; } else { return false; } m_body += conversion( m_typeResolver->realType(), m_state.accumulatorOut().storedType(), expression); m_body += u";\n"_qs; m_body += u"}\n"_qs; return true; } void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int argc, int argv) { INJECT_TRACE_INFO(generate_CallPropertyLookup); if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue) reject(u"call to untyped JavaScript function"_qs); const QQmlJSRegisterContent baseType = registerType(base); if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { const QString name = m_jsUnitGenerator->stringForIndex( m_jsUnitGenerator->lookupNameIndex(index)); if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) { if (inlineMathMethod(name, argc, argv)) return; } reject(u"call to property '%1' of %2"_qs.arg(name, baseType.descriptiveName())); } m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); const QString indexString = QString::number(index); m_body += u"{\n"_qs; QString outVar; m_body += argumentsList(argc, argv, &outVar); const QString lookup = u"aotContext->callObjectPropertyLookup("_qs + indexString + u", "_qs + use(registerVariable(base)) + u", args, types, "_qs + QString::number(argc) + u')'; const QString initialization = u"aotContext->initCallObjectPropertyLookup("_qs + indexString + u')'; generateLookup(lookup, initialization); generateMoveOutVar(outVar); m_body += u"}\n"_qs; } void QQmlJSCodeGenerator::generate_CallElement(int base, int index, int argc, int argv) { Q_UNUSED(base) Q_UNUSED(index) Q_UNUSED(argc) Q_UNUSED(argv) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_CallName(int name, int argc, int argv) { Q_UNUSED(name); Q_UNUSED(argc); Q_UNUSED(argv); reject(u"CallName"_qs); } 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"_qs); } void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) { INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup); if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue) reject(u"call to untyped JavaScript function"_qs); m_body.setHasSideEffects(true); const QString indexString = QString::number(index); m_body += u"{\n"_qs; QString outVar; m_body += argumentsList(argc, argv, &outVar); const QString lookup = u"aotContext->callQmlContextPropertyLookup("_qs + indexString + u", args, types, "_qs + QString::number(argc) + u')'; const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_qs + indexString + u')'; generateLookup(lookup, initialization); generateMoveOutVar(outVar); m_body += u"}\n"_qs; } 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) { Q_UNUSED(func); Q_UNUSED(argc); Q_UNUSED(argv); reject(u"Construct"_qs); } 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"SetUnwindHandlerh"_qs); } void QQmlJSCodeGenerator::generate_UnwindDispatch() { reject(u"UnwindDispatch"_qs); } 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) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_ThrowException() { INJECT_TRACE_INFO(generate_ThrowException); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); generateSetInstructionPointer(); m_body += u"aotContext->engine->throwError("_qs + conversion(m_state.accumulatorIn(), m_typeResolver->globalType( m_typeResolver->jsValueType()), use(m_state.accumulatorVariableIn)) + u");\n"_qs; m_body += u"return "_qs + errorReturnValue() + u";\n"_qs; } void QQmlJSCodeGenerator::generate_GetException() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_SetException() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_CreateCallContext() { INJECT_TRACE_INFO(generate_CreateCallContext); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); m_body += u"{\n"_qs; } void QQmlJSCodeGenerator::generate_PushCatchContext(int index, int nameIndex) { Q_UNUSED(index) Q_UNUSED(nameIndex) reject(u"PushCatchContext"_qs); } 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); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); // Add a semicolon before the closing brace, in case there was a bare label before it. m_body += u";}\n"_qs; } void QQmlJSCodeGenerator::generate_GetIterator(int iterator) { Q_UNUSED(iterator) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_IteratorNext(int value, int done) { Q_UNUSED(value) Q_UNUSED(done) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object) { Q_UNUSED(iterator) Q_UNUSED(object) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_IteratorClose(int done) { Q_UNUSED(done) 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"_qs); } void QQmlJSCodeGenerator::generate_TypeofValue() { reject(u"TypeofValue"_qs); } void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable) { Q_UNUSED(varName) Q_UNUSED(isDeletable) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args) { Q_UNUSED(args); if (argc > 0) reject(u"DefineArray"_qs); // No need to generate code. We don't store empty lists. if (m_typeResolver->equals(m_state.accumulatorOut().storedType(), m_typeResolver->emptyListType())) { return; } m_body += m_state.accumulatorVariableOut + u" = "_qs; m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(), QString()); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args) { Q_UNUSED(internalClassId) Q_UNUSED(argc) Q_UNUSED(args) reject(u"DefineObjectLiteral"_qs); } 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() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_LoadSuperConstructor() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_ToObject() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_Jump(int offset) { INJECT_TRACE_INFO(generate_Jump); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); generateJumpCodeWithTypeConversions(offset, JumpMode::Unconditional); m_body += u";\n"_qs; m_skipUntilNextLabel = true; } void QQmlJSCodeGenerator::generate_JumpTrue(int offset) { INJECT_TRACE_INFO(generate_JumpTrue); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); m_body += u"if ("_qs; m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(), use(m_state.accumulatorVariableIn)); m_body += u") "_qs; generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_JumpFalse(int offset) { INJECT_TRACE_INFO(generate_JumpFalse); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); m_body += u"if (!"_qs; m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(), use(m_state.accumulatorVariableIn)); m_body += u") "_qs; generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_JumpNoException(int offset) { INJECT_TRACE_INFO(generate_JumpNoException); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); m_body += u"if (!context->engine->hasException()) "_qs; generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_JumpNotUndefined(int offset) { Q_UNUSED(offset) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_CheckException() { INJECT_TRACE_INFO(generate_CheckException); m_body.setHasSideEffects(true); m_body.setWriteRegister(QString()); generateExceptionCheck(); } void QQmlJSCodeGenerator::generate_CmpEqNull() { INJECT_TRACE_INFO(generate_CmlEqNull); m_body += m_state.accumulatorVariableOut; m_body += u" = QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs; m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(), use(m_state.accumulatorVariableIn)); m_body += u')'; m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_CmpNeNull() { INJECT_TRACE_INFO(generate_CmlNeNull); m_body += m_state.accumulatorVariableOut; m_body += u" = !QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs; m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(), use(m_state.accumulatorVariableIn)); m_body += u')'; m_body += u";\n"_qs; } QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst) { if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType())) return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn); if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) { return QString::number(lhsConst) + u" == "_qs + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(), use(m_state.accumulatorVariableIn)); } if (m_typeResolver->isNumeric(m_state.accumulatorIn())) { return conversion(m_typeResolver->intType(), m_typeResolver->realType(), QString::number(lhsConst)) + u" == "_qs + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(), use(m_state.accumulatorVariableIn)); } QString result; result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(), QString::number(lhsConst)); result += u".equals("_qs; result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(), use(m_state.accumulatorVariableIn)); result += u')'; return result; } QString QQmlJSCodeGenerator::getLookupPreparation( const QQmlJSRegisterContent &content, const QString &var, int lookup) { if (m_typeResolver->registerContains(content, content.storedType())) return QString(); if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) { return var + u" = QVariant(aotContext->lookupResultMetaType("_qs + QString::number(lookup) + u"))"_qs; } // TODO: We could make sure they're compatible, for example QObject pointers. return QString(); } QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var) { const QQmlJSScope::ConstPtr stored = content.storedType(); if (m_typeResolver->registerContains(content, stored)) return u'&' + var; else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) return var + u".data()"_qs; else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u'&' + var; else reject(u"content pointer of non-QVariant wrapper type "_qs + content.descriptiveName()); return QString(); } QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var) { const QQmlJSScope::ConstPtr stored = content.storedType(); const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType( m_typeResolver->containedType(content)); if (m_typeResolver->equals(contained, stored)) return metaTypeFromType(stored); else if (m_typeResolver->equals(stored, m_typeResolver->varType())) return var + u".metaType()"_qs; // We expect the QVariant to be initialized else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return metaTypeFromName(contained); else reject(u"content type of non-QVariant wrapper type "_qs + content.descriptiveName()); return QString(); } void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst) { INJECT_TRACE_INFO(generate_CmpEqInt); m_body += m_state.accumulatorVariableOut + u" = "_qs + eqIntExpression(lhsConst) + u";\n"_qs; } void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst) { INJECT_TRACE_INFO(generate_CmpNeInt); m_body += m_state.accumulatorVariableOut + u" = !("_qs + eqIntExpression(lhsConst) + u");\n"_qs; } void QQmlJSCodeGenerator::generate_CmpEq(int lhs) { INJECT_TRACE_INFO(generate_CmpEq); generateEqualityOperation(lhs, u"equals"_qs, false); } void QQmlJSCodeGenerator::generate_CmpNe(int lhs) { INJECT_TRACE_INFO(generate_CmpNe); generateEqualityOperation(lhs, u"equals"_qs, true); } void QQmlJSCodeGenerator::generate_CmpGt(int lhs) { INJECT_TRACE_INFO(generate_CmpGt); generateCompareOperation(lhs, u">"_qs); } void QQmlJSCodeGenerator::generate_CmpGe(int lhs) { INJECT_TRACE_INFO(generate_CmpGe); generateCompareOperation(lhs, u">="_qs); } void QQmlJSCodeGenerator::generate_CmpLt(int lhs) { INJECT_TRACE_INFO(generate_CmpLt); generateCompareOperation(lhs, u"<"_qs); } void QQmlJSCodeGenerator::generate_CmpLe(int lhs) { INJECT_TRACE_INFO(generate_CmpLe); generateCompareOperation(lhs, u"<="_qs); } void QQmlJSCodeGenerator::generate_CmpStrictEqual(int lhs) { INJECT_TRACE_INFO(generate_CmpStrictEqual); generateEqualityOperation(lhs, u"strictlyEquals"_qs, false); } void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(int lhs) { INJECT_TRACE_INFO(generate_CmpStrictNotEqual); generateEqualityOperation(lhs, u"strictlyEquals"_qs, true); } void QQmlJSCodeGenerator::generate_CmpIn(int lhs) { Q_UNUSED(lhs) reject(u"CmpIn"_qs); } 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 = use(registerVariable(lhs)); const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(m_state.readRegister(lhs)); m_body += m_state.accumulatorVariableOut + u" = "_qs; if (m_typeResolver->equals( m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType()) && contained->isComposite()) { m_body += conversion( m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(), use(m_state.accumulatorVariableIn) + u"->cast("_qs + input + u')'); } else { m_body += conversion( m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(), u'(' + metaObject(contained) + u")->cast("_qs + input + u')'); } m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_UNot() { INJECT_TRACE_INFO(generate_UNot); generateUnaryOperation(u"!"_qs); } void QQmlJSCodeGenerator::generate_UPlus() { INJECT_TRACE_INFO(generate_UPlus); generateUnaryOperation(u"+"_qs); } void QQmlJSCodeGenerator::generate_UMinus() { INJECT_TRACE_INFO(generate_UMinus); generateUnaryOperation(u"-"_qs); } void QQmlJSCodeGenerator::generate_UCompl() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_Increment() { INJECT_TRACE_INFO(generate_Increment); generateInPlaceOperation(u"++"_qs); } void QQmlJSCodeGenerator::generate_Decrement() { INJECT_TRACE_INFO(generate_Decrement); generateInPlaceOperation(u"--"_qs); } void QQmlJSCodeGenerator::generate_Add(int lhs) { INJECT_TRACE_INFO(generate_Add); generateArithmeticOperation(lhs, u"+"_qs); } void QQmlJSCodeGenerator::generate_BitAnd(int lhs) { Q_UNUSED(lhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_BitOr(int lhs) { Q_UNUSED(lhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_BitXor(int lhs) { Q_UNUSED(lhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_UShr(int lhs) { Q_UNUSED(lhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_Shr(int lhs) { Q_UNUSED(lhs); reject(u"Shr"_qs); } void QQmlJSCodeGenerator::generate_Shl(int lhs) { Q_UNUSED(lhs); reject(u"Shl"_qs); } void QQmlJSCodeGenerator::generate_BitAndConst(int rhs) { Q_UNUSED(rhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_BitOrConst(int rhs) { Q_UNUSED(rhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_BitXorConst(int rhs) { Q_UNUSED(rhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_UShrConst(int rhs) { Q_UNUSED(rhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_ShrConst(int value) { Q_UNUSED(value); reject(u"ShrConst"_qs); } void QQmlJSCodeGenerator::generate_ShlConst(int value) { Q_UNUSED(value); reject(u"ShlConst"_qs); } void QQmlJSCodeGenerator::generate_Exp(int lhs) { Q_UNUSED(lhs) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_Mul(int lhs) { INJECT_TRACE_INFO(generate_Mul); generateArithmeticOperation(lhs, u"*"_qs); } void QQmlJSCodeGenerator::generate_Div(int lhs) { INJECT_TRACE_INFO(generate_Div); generateArithmeticOperation(lhs, u"/"_qs); } void QQmlJSCodeGenerator::generate_Mod(int lhs) { INJECT_TRACE_INFO(generate_Mod); const auto lhsVar = conversion( registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(), use(registerVariable(lhs))); const auto rhsVar = conversion( m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(), use(m_state.accumulatorVariableIn)); Q_ASSERT(!lhsVar.isEmpty()); Q_ASSERT(!rhsVar.isEmpty()); m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(), u'(' + lhsVar + u" % "_qs + rhsVar + u')'); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generate_Sub(int lhs) { INJECT_TRACE_INFO(generate_Sub); generateArithmeticOperation(lhs, u"-"_qs); } void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) { Q_UNUSED(firstReg) Q_UNUSED(count) BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined() { BYTECODE_UNIMPLEMENTED(); } void QQmlJSCodeGenerator::generate_GetTemplateObject(int index) { Q_UNUSED(index) BYTECODE_UNIMPLEMENTED(); } static bool instructionManipulatesContext(QV4::Moth::Instr::Type type) { using Type = QV4::Moth::Instr::Type; switch (type) { case Type::PopContext: case Type::PopScriptContext: case Type::CreateCallContext: case Type::CreateCallContext_Wide: case Type::PushCatchContext: case Type::PushCatchContext_Wide: case Type::PushWithContext: case Type::PushWithContext_Wide: case Type::PushBlockContext: case Type::PushBlockContext_Wide: case Type::CloneBlockContext: case Type::CloneBlockContext_Wide: case Type::PushScriptContext: case Type::PushScriptContext_Wide: return true; default: break; } return false; } 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().storedType())) { m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator) .value(accumulatorIn.value().storedType()); Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty()); } else { m_state.accumulatorVariableIn.clear(); } auto labelIt = m_labels.constFind(currentInstructionOffset()); if (labelIt != m_labels.constEnd()) { nextSection(); m_body.setHasSideEffects(true); m_body.setLabel(*labelIt); m_body += *labelIt + u":;\n"_qs; m_skipUntilNextLabel = false; } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) { return SkipInstruction; } nextSection(); if (m_state.changedRegisterIndex() == Accumulator) m_state.accumulatorVariableOut = changedRegisterVariable(); else m_state.accumulatorVariableOut.clear(); if (!m_state.accumulatorVariableOut.isEmpty()) m_body.setWriteRegister(m_state.accumulatorVariableOut); // 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())); const int currentLine = currentSourceLocation().startLine; if (currentLine != m_lastLineNumberUsed) { const int nextLine = nextJSLine(currentLine); for (auto line = currentLine - 1; line < nextLine - 1; ++line) { m_body += u"// "_qs; m_body += m_sourceCodeLines.value(line).trimmed(); m_body += u'\n'; } m_lastLineNumberUsed = currentLine; } // 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.hasSideEffects() && m_state.changedRegisterIndex() == InvalidRegister) return SkipInstruction; return ProcessInstruction; } void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type) { generateJumpCodeWithTypeConversions(0, JumpMode::None); nextSection(); } void QQmlJSCodeGenerator::generateSetInstructionPointer() { m_body += u"aotContext->setInstructionPointer("_qs + QString::number(nextInstructionOffset()) + u");\n"_qs; } void QQmlJSCodeGenerator::generateExceptionCheck() { m_body += u"if (aotContext->engine->hasError())\n"_qs; m_body += u" return "_qs + errorReturnValue() + u";\n"_qs; } void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &function, bool invert) { const QQmlJSRegisterContent lhsContent = registerType(lhs); auto isComparable = [&]() { if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(m_state.accumulatorIn())) { return true; } if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration()) return true; if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration()) return true; return false; }; if (!isComparable()) { reject(u"equality comparison on non-primitive types %1 and %2"_qs.arg( m_state.accumulatorIn().descriptiveName(), lhsContent.descriptiveName())); } const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType(); const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType(); m_body += m_state.accumulatorVariableOut + u" = "_qs; const auto primitive = m_typeResolver->jsPrimitiveType(); if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) { m_body += use(registerVariable(lhs)); m_body += (invert ? u" != "_qs : u" == "_qs); m_body += use(m_state.accumulatorVariableIn); } else { if (invert) m_body += u'!'; m_body += conversion(registerType(lhs).storedType(), primitive, use(registerVariable(lhs))); m_body += u'.'; m_body += function; m_body += u'('; m_body += conversion(m_state.accumulatorIn().storedType(), primitive, use(m_state.accumulatorVariableIn)); m_body += u')'; } m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator) { m_body += m_state.accumulatorVariableOut; const auto lhsType = registerType(lhs); const QQmlJSScope::ConstPtr compareType = m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn()) ? m_typeResolver->merge(lhsType, m_state.accumulatorIn()).storedType() : m_typeResolver->jsPrimitiveType(); m_body += u" = "_qs; m_body += conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs))); m_body += u' '; m_body += cppOperator; m_body += u' '; m_body += conversion(m_state.accumulatorIn().storedType(), compareType, use(m_state.accumulatorVariableIn)); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator) { const auto lhsVar = conversion(registerType(lhs), m_state.readRegister(lhs), use(registerVariable(lhs))); const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), use(m_state.accumulatorVariableIn)); Q_ASSERT(!lhsVar.isEmpty()); Q_ASSERT(!rhsVar.isEmpty()); m_body += m_state.accumulatorVariableOut; m_body += u" = "_qs; m_body += conversion( m_typeResolver->original(m_state.accumulatorOut()), m_state.accumulatorOut(), u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')'); m_body += u";\n"_qs; } void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator) { const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), use(m_state.accumulatorVariableIn)); if (var == m_state.accumulatorVariableOut) { m_body += m_state.accumulatorVariableOut + u" = "_qs + cppOperator + var + u";\n"_qs; return; } const auto original = m_typeResolver->original(m_state.accumulatorOut()); if (m_state.accumulatorOut() == original) { m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs; m_body += m_state.accumulatorVariableOut + u" = "_qs + cppOperator + m_state.accumulatorVariableOut + u";\n"_qs; return; } m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion( m_typeResolver->original(m_state.accumulatorOut()), m_state.accumulatorOut(), cppOperator + var) + u";\n"_qs; } void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator) { const auto var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(), use(m_state.accumulatorVariableIn)); if (var == m_state.accumulatorVariableOut) { m_body += cppOperator + var + u";\n"_qs; return; } const auto original = m_typeResolver->original(m_state.accumulatorOut()); if (m_state.accumulatorOut() == original) { m_body += m_state.accumulatorVariableOut + u" = "_qs + var + u";\n"_qs; m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_qs; return; } m_body += u"{\n"_qs; m_body += u"auto converted = "_qs + var + u";\n"_qs; m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion( m_typeResolver->original(m_state.accumulatorOut()), m_state.accumulatorOut(), cppOperator + u"converted"_qs) + u";\n"_qs; m_body += u"}\n"_qs; } void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization, const QString &resultPreparation) { if (!resultPreparation.isEmpty()) m_body += resultPreparation + u";\n"_qs; m_body += u"while (!"_qs + lookup + u") {\n"_qs; generateSetInstructionPointer(); m_body += initialization + u";\n"_qs; generateExceptionCheck(); if (!resultPreparation.isEmpty()) m_body += resultPreparation + u";\n"_qs; m_body += u"}\n"_qs; } void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions( int relativeOffset, JumpMode mode) { nextSection(); m_body.setHasSideEffects(true); m_body += u"{\n"_qs; int absoluteOffset =nextInstructionOffset() + relativeOffset; const auto annotation = m_annotations->find(absoluteOffset); if (annotation != m_annotations->constEnd()) { const auto &conversions = annotation->second.typeConversions; for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd(); regIt != regEnd; ++regIt) { int registerIndex = regIt.key(); const QQmlJSRegisterContent targetType = regIt.value(); if (!targetType.isValid()) continue; QQmlJSRegisterContent currentType; QString currentVariable; if (registerIndex == m_state.changedRegisterIndex()) { currentType = m_state.changedRegister(); currentVariable = changedRegisterVariable(); } else { auto it = m_state.registers.find(registerIndex); if (it == m_state.registers.end()) continue; currentType = it.value(); currentVariable = registerVariable(registerIndex); } // Actually == here. We want the jump code also for equal types if (currentType == targetType || !isTypeStorable(m_typeResolver, targetType.storedType())) { continue; } Q_ASSERT(m_registerVariables.contains(registerIndex)); const auto ¤tRegisterVariables = m_registerVariables[registerIndex]; const auto variable = currentRegisterVariables.constFind(targetType.storedType()); if (variable == currentRegisterVariables.end() || *variable == currentVariable) continue; nextSection(); m_body.setWriteRegister(*variable); m_body += *variable; m_body += u" = "_qs; m_body += conversion(currentType, targetType, use(currentVariable)); m_body += u";\n"_qs; } } nextSection(); m_body.setHasSideEffects(true); if (relativeOffset) { auto labelIt = m_labels.find(absoluteOffset); if (labelIt == m_labels.end()) labelIt = m_labels.insert(absoluteOffset, u"label_%1"_qs.arg(m_labels.count())); m_body.setJump(*labelIt, mode); m_body += u" goto "_qs + *labelIt + u";\n"_qs; } m_body += u"}\n"_qs; } QString QQmlJSCodeGenerator::registerVariable(int index) const { if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) { const int argumentIndex = index - QV4::CallData::OffsetCount; return u"*static_cast<"_qs + castTargetName(m_function->argumentTypes[argumentIndex]) + u"*>(argumentsPtr["_qs + QString::number(argumentIndex) + u"])"_qs; } return m_registerVariables.value(index).value(registerType(index).storedType()); } QString QQmlJSCodeGenerator::changedRegisterVariable() const { return m_registerVariables.value(m_state.changedRegisterIndex()).value( m_state.changedRegister().storedType()); } QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const { if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex()) { return m_typeResolver->globalType( m_function->argumentTypes[index - QV4::CallData::OffsetCount]); } return m_state.registers[index]; } QString QQmlJSCodeGenerator::conversion(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 zeroBoolOrNumeric = [&](const QQmlJSScope::ConstPtr &to) { if (m_typeResolver->equals(to, boolType)) return u"false"_qs; if (m_typeResolver->equals(to, m_typeResolver->intType())) return u"0"_qs; if (m_typeResolver->equals(to, m_typeResolver->floatType())) return u"0.0f"_qs; if (m_typeResolver->equals(to, m_typeResolver->realType())) return u"0.0"_qs; return QString(); }; if (m_typeResolver->equals(from, m_typeResolver->voidType())) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs; const QString zero = zeroBoolOrNumeric(to); if (!zero.isEmpty()) return zero; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"undefined"_qs); if (m_typeResolver->equals(from, to)) return QString(); // Anything else is just the default constructed type. return to->augmentedInternalName() + u"()"_qs; } if (m_typeResolver->equals(from, m_typeResolver->nullType())) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs; if (m_typeResolver->equals(to, jsValueType)) return u"QJSValue(QJSValue::NullValue)"_qs; if (m_typeResolver->equals(to, jsPrimitiveType)) return u"QJSPrimitiveValue(QJSPrimitiveNull())"_qs; if (m_typeResolver->equals(to, varType)) return u"QVariant::fromValue(nullptr)"_qs; const QString zero = zeroBoolOrNumeric(to); if (!zero.isEmpty()) return zero; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return QQmlJSUtils::toLiteral(u"null"_qs); if (m_typeResolver->equals(from, to)) return QString(); reject(u"Conversion from null to %1"_qs.arg(to->internalName())); } if (m_typeResolver->equals(from, m_typeResolver->emptyListType())) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) return castTargetName(to) + u"()"_qs; if (m_typeResolver->equals(to, m_typeResolver->varType())) return u"QVariant(QVariantList())"_qs; if (m_typeResolver->equals(from, to)) return QString(); reject(u"Conversion from empty list to %1"_qs.arg(to->internalName())); } if (m_typeResolver->equals(from, to)) return variable; if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) { if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) { for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) { // We still have to cast as other execution paths may result in different types. if (m_typeResolver->equals(base, to)) return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')'; } for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) { if (m_typeResolver->equals(base, from)) return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')'; } } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) { return u'(' + variable + u" != nullptr)"_qs; } } auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) { return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript(); }; if (isJsValue(from) && isJsValue(to)) return variable; const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) { return m_typeResolver->isNumeric(m_typeResolver->globalType(type)) || m_typeResolver->equals(type, m_typeResolver->boolType()) || type->scopeType() == QQmlJSScope::EnumScope; }; if (m_typeResolver->equals(from, m_typeResolver->realType()) && m_typeResolver->equals(to, m_typeResolver->intType())) { return u"QJSNumberCoercion::toInteger("_qs + variable + u')'; } if (isBoolOrNumber(from) && isBoolOrNumber(to)) return to->internalName() + u'(' + variable + u')'; if (m_typeResolver->equals(from, jsPrimitiveType)) { if (m_typeResolver->equals(to, m_typeResolver->realType())) return variable + u".toDouble()"_qs; if (m_typeResolver->equals(to, boolType)) return variable + u".toBoolean()"_qs; if (m_typeResolver->equals(to, m_typeResolver->intType())) return variable + u".toInteger()"_qs; if (m_typeResolver->equals(to, m_typeResolver->stringType())) return variable + u".toString()"_qs; if (m_typeResolver->equals(to, jsValueType)) return u"QJSValue(QJSPrimitiveValue("_qs + variable + u"))"_qs; if (m_typeResolver->equals(to, varType)) return variable + u".toVariant()"_qs; if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) return u"static_cast<"_qs + to->internalName() + u" *>(nullptr)"_qs; } if (isJsValue(from)) { if (m_typeResolver->equals(to, jsPrimitiveType)) return variable + u".toPrimitive()"_qs; if (m_typeResolver->equals(to, varType)) return variable + u".toVariant(QJSValue::RetainJSObjects)"_qs; return u"qjsvalue_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')'; } if (m_typeResolver->equals(to, jsPrimitiveType)) return u"QJSPrimitiveValue("_qs + variable + u')'; if (m_typeResolver->equals(to, jsValueType)) return u"aotContext->engine->toScriptValue("_qs + variable + u')'; if (m_typeResolver->equals(from, varType)) { if (m_typeResolver->equals(to, m_typeResolver->listPropertyType())) return u"QQmlListReference("_qs + variable + u", aotContext->qmlEngine())"_qs; return u"aotContext->engine->fromVariant<"_qs + castTargetName(to) + u">("_qs + variable + u')'; } if (m_typeResolver->equals(to, varType)) return u"QVariant::fromValue("_qs + variable + u')'; if (m_typeResolver->equals(from, m_typeResolver->urlType()) && m_typeResolver->equals(to, m_typeResolver->stringType())) { return variable + u".toString()"_qs; } if (m_typeResolver->equals(from, m_typeResolver->stringType()) && m_typeResolver->equals(to, m_typeResolver->urlType())) { return u"QUrl("_qs + variable + u')'; } const auto retrieveFromPrimitive = [&](const QQmlJSScope::ConstPtr &type) { if (m_typeResolver->equals(type, m_typeResolver->boolType())) return u".toBool()"_qs; if (m_typeResolver->equals(type, m_typeResolver->intType())) return u".toInteger()"_qs; if (m_typeResolver->equals(type, m_typeResolver->realType())) return u".toDouble()"_qs; if (m_typeResolver->equals(type, m_typeResolver->stringType())) return u".toString()"_qs; return QString(); }; const auto fitsIntoPrimitive = [&](const QQmlJSScope::ConstPtr &type) { return !retrieveFromPrimitive(type).isEmpty() || m_typeResolver->equals(type, m_typeResolver->floatType()); }; if (fitsIntoPrimitive(from)) { const QString retrieve = retrieveFromPrimitive(to); if (!retrieve.isEmpty()) return u"QJSPrimitiveValue("_qs + variable + u')' + retrieve; } // TODO: more efficient string conversions, possibly others return u"aotContext->engine->fromScriptValue<"_qs + castTargetName(to) + u">(aotContext->engine->toScriptValue("_qs + variable + u"))"_qs; } int QQmlJSCodeGenerator::nextJSLine(uint line) const { auto findLine = [](uint line, const QV4::CompiledData::CodeOffsetToLine &entry) { return entry.line > line; }; const auto codeToLine = std::upper_bound(m_context->lineNumberMapping.constBegin(), m_context->lineNumberMapping.constEnd(), line, findLine); bool bNoNextLine = m_context->lineNumberMapping.constEnd() == codeToLine; return static_cast(bNoNextLine ? -1 : codeToLine->line); } void QQmlJSCodeGenerator::reject(const QString &thing) { setError(u"Cannot generate efficient code for %1"_qs.arg(thing)); } QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator) : accumulatorOut(generator->m_state.accumulatorOut()) , accumulatorVariableIn(generator->m_state.accumulatorVariableIn) , accumulatorVariableOut(generator->m_state.accumulatorVariableOut) , generator(generator) { const QQmlJSTypeResolver *resolver = generator->m_typeResolver; const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut); const QQmlJSScope::ConstPtr origStored = resolver->originalType(accumulatorOut.storedType()); if (!resolver->equals(origContained, resolver->containedType(accumulatorOut)) || !resolver->equals(origStored, accumulatorOut.storedType())) { generator->m_state.accumulatorVariableOut = u"retrieved"_qs; generator->m_state.setRegister(Accumulator, resolver->original(accumulatorOut)); generator->m_body += u"{\n"_qs; 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) && resolver->registerIsStoredIn( generator->m_state.accumulatorOut(), 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->use( generator->m_state.accumulatorVariableIn) + u"_moved"_qs; generator->m_body += u"{\n"_qs; generator->m_body += u"QVariant "_qs + generator->m_state.accumulatorVariableIn + u" = std::move("_qs + generator->m_state.accumulatorVariableOut + u");\n"_qs; } } QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter() { if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) { generator->m_body += accumulatorVariableOut + u" = "_qs + generator->conversion( generator->m_state.accumulatorOut(), accumulatorOut, generator->m_state.accumulatorVariableOut) + u";\n"_qs; generator->m_body += u"}\n"_qs; generator->m_state.setRegister(Accumulator, accumulatorOut); generator->m_state.accumulatorVariableOut = accumulatorVariableOut; } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) { generator->m_body += u"}\n"_qs; generator->m_state.accumulatorVariableIn = accumulatorVariableIn; } } QT_END_NAMESPACE