aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljscodegenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljscodegenerator.cpp')
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp1130
1 files changed, 725 insertions, 405 deletions
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index f665b1ae71..f7fa9ba403 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -34,6 +34,7 @@
#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>
@@ -83,13 +84,26 @@ QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type)
QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
{
- if (!objectType->isComposite())
+ 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)
@@ -98,34 +112,41 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
m_function = function;
m_error = error;
- QSet<QString> registerNames;
- for (const InstructionAnnotation &annotation : *m_annotations) {
- for (auto regIt = annotation.registers.constBegin(),
- regEnd = annotation.registers.constEnd();
- regIt != regEnd;
- ++regIt) {
- int registerIndex = regIt.key();
+ QHash<int, QHash<QQmlJSScope::ConstPtr, QString>> registerNames;
- const QQmlJSScope::ConstPtr seenType = regIt.value().storedType();
- // Don't generate any variables for registers that are initialized with undefined.
- if (seenType.isNull() || seenType == m_typeResolver->voidType())
- continue;
+ 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)) {
- QString variableName = u"r%1"_qs.arg(registerIndex);
- if (registerNames.contains(variableName))
- variableName += u'_' + QString::number(typesForRegisters.count());
- registerNames.insert(variableName);
- typesForRegisters[seenType] = variableName;
- }
+ auto &typesForRegisters = m_registerVariables[registerIndex];
+ if (!typesForRegisters.contains(seenType)) {
+ auto &currentRegisterNames = 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));
const QByteArray byteCode = function->code;
decode(byteCode.constData(), static_cast<uint>(byteCode.length()));
eliminateDeadStores();
@@ -133,11 +154,16 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
QQmlJSAotFunction result;
result.includes.swap(m_includes);
+ QDuplicateTracker<QString> generatedVariables;
for (const auto &registerTypes : 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)
@@ -153,9 +179,9 @@ QQmlJSAotFunction QQmlJSCodeGenerator::run(
for (const Section &section : m_sections)
result.code += section.code();
- for (const QQmlJSScope::ConstPtr &argType : qAsConst(function->argumentTypes)) {
- if (argType) {
- result.argumentTypes.append(argType->augmentedInternalName());
+ for (const QQmlJSRegisterContent &argType : qAsConst(function->argumentTypes)) {
+ if (argType.isValid()) {
+ result.argumentTypes.append(argType.storedType()->augmentedInternalName());
} else {
result.argumentTypes.append(u"void"_qs);
}
@@ -184,9 +210,11 @@ QList<QQmlJSCodeGenerator::BasicBlock> QQmlJSCodeGenerator::findBasicBlocks(
const QQmlJSCodeGenerator::Section &section = sections[i];
const QString label = section.label();
if (!label.isEmpty() || currentBlock.jumpMode != JumpMode::None) {
- currentBlock.endSection = i;
- basicBlocks.append(currentBlock);
- currentBlock.beginSection = i;
+ if (currentBlock.beginSection != i) {
+ currentBlock.endSection = i;
+ basicBlocks.append(currentBlock);
+ currentBlock.beginSection = i;
+ }
currentBlock.label = label;
}
currentBlock.jumpMode = section.jumpMode();
@@ -320,6 +348,7 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
usedOnce = true;
}
}
+ requiredRegisters[0][variable] = inUse;
if (!usedOnce) {
registerTypeIt = registerTypes.erase(registerTypeIt);
@@ -332,7 +361,8 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
// Sort the offsets in reverse order so that we can pop from the back
std::sort(toErase.begin(), toErase.end());
- int eraseIndex = toErase.length() - 1;
+ 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;
@@ -361,12 +391,10 @@ void QQmlJSCodeGenerator::eliminateDeadStores()
} while (!toErase.isEmpty() || foundUnknownBlock);
}
-QString QQmlJSCodeGenerator::errorReturnValue() const
+QString QQmlJSCodeGenerator::errorReturnValue()
{
- if (m_function->returnType) {
- return conversion(m_typeResolver->jsPrimitiveType(), m_function->returnType,
- u"QJSPrimitiveValue(QJSPrimitiveUndefined())"_qs);
- }
+ if (m_function->returnType)
+ return conversion(m_typeResolver->voidType(), m_function->returnType, QString());
return QString();
}
@@ -381,29 +409,29 @@ void QQmlJSCodeGenerator::generate_Ret()
const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_qs;
if (!m_state.accumulatorVariableIn.isEmpty()) {
const QString in = use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->varType()) {
+ 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_state.accumulatorIn.storedType() == m_typeResolver->jsPrimitiveType()) {
+ } 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_state.accumulatorIn.storedType() == m_typeResolver->jsValueType()) {
+ } 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_function->returnType != m_typeResolver->voidType()
- && m_function->returnType->internalName() != u"void"_qs) {
- if (m_function->returnType->internalName().trimmed().endsWith(u'*')
- || m_function->returnType->internalName().trimmed().endsWith(u'&')) {
- setError(u"Not all paths return a value"_qs);
- return;
+ + 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 += signalUndefined;
- m_body += u"return "_qs + m_function->returnType->internalName() + u"()"_qs;
+ m_body += u"return "_qs + conversion(
+ m_state.accumulatorIn().storedType(), m_function->returnType, QString());
}
} else {
m_body += u"return"_qs;
@@ -426,6 +454,19 @@ static QString toNumericString(double value)
return QString::number(i);
}
+ switch (qFpClassify(value)) {
+ case FP_INFINITE: {
+ const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
+ return std::signbit(value) ? (u'-' + inf) : inf;
+ }
+ case FP_NAN:
+ return u"std::numeric_limits<double>::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<double>::max_digits10);
}
@@ -436,9 +477,14 @@ void QQmlJSCodeGenerator::generate_LoadConst(int index)
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
+
+ // ### handle other types?
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ toNumericString(value));
+
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadZero()
@@ -446,8 +492,10 @@ void QQmlJSCodeGenerator::generate_LoadZero()
INJECT_TRACE_INFO(generate_LoadZero);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = 0"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->intType(), m_state.accumulatorOut().storedType(), u"0"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_LoadTrue()
@@ -455,8 +503,10 @@ void QQmlJSCodeGenerator::generate_LoadTrue()
INJECT_TRACE_INFO(generate_LoadTrue);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = true"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"true"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadFalse()
@@ -464,43 +514,53 @@ void QQmlJSCodeGenerator::generate_LoadFalse()
INJECT_TRACE_INFO(generate_LoadFalse);
m_body += m_state.accumulatorVariableOut;
- m_body += u" = false"_qs;
+ m_body += u" = "_qs + conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(), u"false"_qs);
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_LoadNull()
{
INJECT_TRACE_INFO(generate_LoadNull);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = nullptr"_qs;
+ 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;
+ generateOutputVariantConversion(m_typeResolver->nullType());
}
void QQmlJSCodeGenerator::generate_LoadUndefined()
{
INJECT_TRACE_INFO(generate_LoadUndefined);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + conversion(
- m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut.storedType(),
- u"QJSPrimitiveValue()"_qs) + u";\n"_qs;
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->voidType());
}
void QQmlJSCodeGenerator::generate_LoadInt(int value)
{
INJECT_TRACE_INFO(generate_LoadInt);
- Q_ASSERT(m_typeResolver->registerContains(m_state.accumulatorOut, m_typeResolver->intType()));
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QString::number(value);
+ m_body += conversion(m_typeResolver->intType(), m_state.accumulatorOut().storedType(),
+ QString::number(value));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->intType());
}
void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
{
INJECT_TRACE_INFO(generate_MoveConst);
- auto var = registerVariable(destTemp);
+ Q_ASSERT(destTemp == m_state.changedRegisterIndex());
+
+ auto var = changedRegisterVariable();
if (var.isEmpty())
return; // Do not load 'undefined'
@@ -508,48 +568,38 @@ void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
const auto v4Value = QV4::StaticValue::fromReturnedValue(
m_jsUnitGenerator->constant(constIndex));
- if (v4Value.isNull()) {
- const auto type = registerType(destTemp).storedType();
- m_body += var + u" = "_qs;
- if (type == m_typeResolver->jsPrimitiveType()) {
- m_body += u"QJSPrimitiveNull()"_qs;
- } else if (type == m_typeResolver->jsValueType()) {
- m_body += u"QJSValue(QJSValue::NullValue)"_qs;
- } else if (type == m_typeResolver->varType()) {
- m_body += u"QVariant::fromValue<std::nullptr_t>(nullptr)"_qs;
- } else if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- m_body += u"nullptr"_qs;
- } else {
- setError(u"Cannot load null into %1"_qs.arg(m_state.accumulatorOut.descriptiveName()));
- }
+ const auto changed = m_state.changedRegister().storedType();
+ QQmlJSScope::ConstPtr contained;
+ QString input;
- m_body += u";\n"_qs;
- return;
+ m_body += var + u" = "_qs;
+ 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"_qs : u"false"_qs;
+ } else if (v4Value.isInteger()) {
+ contained = m_typeResolver->intType();
+ input = QString::number(v4Value.int_32());
+ } else if (v4Value.isDouble()) {
+ contained = m_typeResolver->realType();
+ input = toNumericString(v4Value.doubleValue());
+ } else {
+ reject(u"unknown const type"_qs);
}
-
- double value = 0.0f;
- if (v4Value.isInteger() || v4Value.isBoolean())
- value = v4Value.int_32();
- else if (v4Value.isDouble())
- value = v4Value.doubleValue();
-
- m_body += var;
- m_body += u" = "_qs;
- m_body += toNumericString(value);
- m_body += u";\n"_qs;
+ m_body += conversion(contained, changed, input) + u";\n"_qs;
+ generateOutputVariantConversion(contained);
}
void QQmlJSCodeGenerator::generate_LoadReg(int reg)
{
INJECT_TRACE_INFO(generate_LoadReg);
- // We can't emit any code yet 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 += use(registerVariable(reg));
+ m_body += conversion(registerType(reg), m_state.accumulatorOut(), use(registerVariable(reg)));
m_body += u";\n"_qs;
}
@@ -557,14 +607,15 @@ void QQmlJSCodeGenerator::generate_StoreReg(int reg)
{
INJECT_TRACE_INFO(generate_StoreReg);
- Q_ASSERT(m_state.accumulatorIn.isValid());
- const QString var = registerVariable(reg);
+ 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, registerType(reg),
+ m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
use(m_state.accumulatorVariableIn));
m_body += u";\n"_qs;
}
@@ -573,11 +624,15 @@ void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
{
INJECT_TRACE_INFO(generate_MoveReg);
- const QString destRegName = registerVariable(destReg);
+ 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 += use(registerVariable(srcReg));
+ m_body += conversion(registerType(srcReg), m_state.changedRegister(),
+ use(registerVariable(srcReg)));
m_body += u";\n"_qs;
}
@@ -619,8 +674,11 @@ void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId));
+ m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut().storedType(),
+ QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->stringType());
}
void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
@@ -646,9 +704,11 @@ void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
{
INJECT_TRACE_INFO(generate_LoadGlobalLookup);
+ AccumulatorConverter registers(this);
+
const QString lookup = u"aotContext->loadGlobalLookup("_qs + QString::number(index)
+ u", &"_qs + m_state.accumulatorVariableOut + u", "_qs
- + metaTypeFromType(m_state.accumulatorOut.storedType()) + u')';
+ + metaTypeFromType(m_state.accumulatorOut().storedType()) + u')';
const QString initialization = u"aotContext->initLoadGlobalLookup("_qs
+ QString::number(index) + u')';
generateLookup(lookup, initialization);
@@ -658,55 +718,51 @@ 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) {
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ conversion(
- m_typeResolver->jsValueType(), m_state.accumulatorOut.storedType(),
+ 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) {
+ 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')';
+ + 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();
+ 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) {
- m_body += u"{\n"_qs;
-
- const auto lookupType = contentType(m_state.accumulatorOut, m_state.accumulatorVariableOut);
+ 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')';
+ + 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);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if (m_state.accumulatorOut.isType() || m_state.accumulatorOut.isImportNamespace()) {
+ } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
generateTypeLookup(index);
} else {
- Q_UNREACHABLE();
+ reject(u"lookup of %1"_qs.arg(m_state.accumulatorOut().descriptiveName()));
}
}
@@ -726,10 +782,10 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
switch (type.variant()) {
case QQmlJSRegisterContent::ScopeProperty:
case QQmlJSRegisterContent::ExtensionScopeProperty: {
- if (type.property().type() != m_typeResolver->containedType(m_state.accumulatorIn)) {
+ 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))
+ + 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)
@@ -739,9 +795,9 @@ void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
} else {
m_body += u"aotContext->storeNameSloppy("_qs + QString::number(nameIndex)
+ u", "_qs
- + contentPointer(m_state.accumulatorIn, use(m_state.accumulatorVariableIn))
+ + contentPointer(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn))
+ u", "_qs
- + contentType(m_state.accumulatorIn, use(m_state.accumulatorVariableIn)) + u')';
+ + contentType(m_state.accumulatorIn(), use(m_state.accumulatorVariableIn)) + u')';
m_body += u";\n"_qs;
}
break;
@@ -767,16 +823,18 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
const QQmlJSRegisterContent baseType = registerType(base);
- if (!m_typeResolver->isNumeric(m_state.accumulatorIn) || !baseType.isList()) {
+ if (!m_typeResolver->isNumeric(m_state.accumulatorIn()) || !baseType.isList()) {
reject(u"LoadElement with non-list base type or non-numeric arguments"_qs);
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect LoadElement"_qs);
return;
}
+ AccumulatorConverter registers(this);
+
const QString baseName = use(registerVariable(base));
const QString indexName = use(m_state.accumulatorVariableIn);
@@ -789,7 +847,7 @@ void QQmlJSCodeGenerator::generate_LoadElement(int base)
+ u" < "_qs + baseName + u".count(&"_qs + baseName
+ u"))\n"_qs;
m_body += u" "_qs + m_state.accumulatorVariableOut + u" = "_qs +
- conversion(elementType, m_state.accumulatorOut,
+ conversion(elementType, m_state.accumulatorOut(),
baseName + u".at(&"_qs + baseName + u", "_qs
+ indexName + u')') + u";\n"_qs;
m_body += u"else\n"_qs;
@@ -811,7 +869,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
return;
}
- if (baseType.storedType() != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(baseType, m_typeResolver->listPropertyType())) {
reject(u"indirect StoreElement"_qs);
return;
}
@@ -828,7 +886,7 @@ void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
+ 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))
+ m_body += conversion(m_state.accumulatorIn(), elementType, use(m_state.accumulatorVariableIn))
+ u");\n"_qs;
}
@@ -847,14 +905,14 @@ void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
void QQmlJSCodeGenerator::generateEnumLookup(int index)
{
- const QString enumMember = m_state.accumulatorOut.enumMember();
+ 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();
+ const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
if (metaEnum.hasValues()) {
m_body += m_state.accumulatorVariableOut + u" = "_qs
+ QString::number(metaEnum.value(enumMember));
@@ -862,7 +920,7 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
return;
}
- const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut.scopeType();
+ const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
// Otherwise we would have found an enum with values.
Q_ASSERT(!scopeType->isComposite());
@@ -880,12 +938,13 @@ void QQmlJSCodeGenerator::generateEnumLookup(int index)
void QQmlJSCodeGenerator::generateTypeLookup(int index)
{
const QString indexString = QString::number(index);
+ const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator);
const QString namespaceString
- = m_state.accumulatorIn.isImportNamespace()
- ? QString::number(m_state.accumulatorIn.importNamespace())
+ = accumulatorIn.isImportNamespace()
+ ? QString::number(accumulatorIn.importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- switch (m_state.accumulatorOut.variant()) {
+ switch (m_state.accumulatorOut().variant()) {
case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
@@ -895,7 +954,6 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
break;
}
case QQmlJSRegisterContent::ScopeModulePrefix:
- m_body += m_state.accumulatorVariableOut + u" = aotContext->qmlScopeObject;\n"_qs;
break;
case QQmlJSRegisterContent::ScopeAttached: {
const QString lookup = u"aotContext->loadAttachedLookup("_qs + indexString
@@ -921,33 +979,75 @@ void QQmlJSCodeGenerator::generateTypeLookup(int index)
}
}
+void QQmlJSCodeGenerator::generateOutputVariantConversion(const QQmlJSScope::ConstPtr &containedType)
+{
+ if (changedRegisterVariable().isEmpty())
+ return;
+
+ const QQmlJSRegisterContent changed = m_state.changedRegister();
+ if (!m_typeResolver->equals(changed.storedType(), m_typeResolver->varType()))
+ return;
+
+ const QQmlJSScope::ConstPtr target = m_typeResolver->containedType(changed);
+ if (m_typeResolver->equals(target, containedType)
+ || m_typeResolver->equals(target, m_typeResolver->varType())) {
+ return;
+ }
+
+ // If we could store the type directly, we would not wrap it in a QVariant.
+ // Therefore, our best bet here is metaTypeFromName().
+ m_body += changedRegisterVariable() + u".convert("_qs + metaTypeFromName(target) + u");\n"_qs;
+}
+
void QQmlJSCodeGenerator::generate_GetLookup(int index)
{
INJECT_TRACE_INFO(generate_GetLookup);
- if (m_state.accumulatorOut.isMethod()) {
+ if (m_state.accumulatorOut().isMethod()) {
reject(u"lookup of function property."_qs);
return;
}
- if (m_state.accumulatorOut.isEnumeration()) {
- generateEnumLookup(index);
+ 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;
}
- if (m_state.accumulatorOut.isImportNamespace()) {
- m_body.setWriteRegister(QString());
- return; // Nothing to do. We've resolved the prefix already.
+ 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())
+ const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
+ ? QString::number(m_state.accumulatorIn().importNamespace())
: u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_qs;
- const auto storedType = m_state.accumulatorIn.storedType();
- const bool isReferenceType
- = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
- if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::ObjectAttached) {
+ 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
@@ -964,7 +1064,8 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
+ use(m_state.accumulatorVariableIn) + u')';
generateLookup(lookup, initialization);
return;
- } else if (m_state.accumulatorOut.variant() == QQmlJSRegisterContent::Singleton) {
+ }
+ case QQmlJSRegisterContent::Singleton: {
const QString lookup = u"aotContext->loadSingletonLookup("_qs + indexString
+ u", &"_qs + m_state.accumulatorVariableOut + u')';
const QString initialization = u"aotContext->initLoadSingletonLookup("_qs
@@ -972,51 +1073,50 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
generateLookup(lookup, initialization);
return;
}
+ default:
+ break;
+ }
- Q_ASSERT(m_state.accumulatorOut.isProperty());
+ Q_ASSERT(m_state.accumulatorOut().isProperty());
- const QQmlJSScope::ConstPtr out = m_state.accumulatorOut.storedType();
+ const QQmlJSScope::ConstPtr out = m_state.accumulatorOut().storedType();
if (isReferenceType) {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getObjectLookup("_qs + indexString
+ u", "_qs + use(m_state.accumulatorVariableIn) + u", "_qs
- + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut) + u')';
+ + 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", "_qs + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
+ u')';
const QString preparation = getLookupPreparation(
- m_state.accumulatorOut, m_state.accumulatorVariableOut, index);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
- } else if ((storedType == m_typeResolver->stringType()
- || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
+ } 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 (storedType == m_typeResolver->listPropertyType())
+ if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->listPropertyType()))
m_body += u'&' + use(m_state.accumulatorVariableIn);
m_body += u')' + u";\n"_qs;
- } else if (storedType == m_typeResolver->jsValueType()) {
+ } else if (m_typeResolver->registerIsStoredIn(accumulatorIn, m_typeResolver->jsValueType())) {
reject(u"lookup in QJSValue"_qs);
} else {
- m_body += u"{\n"_qs;
- protectAccumulator();
const QString lookup = u"aotContext->getValueLookup("_qs + indexString
- + u", "_qs + contentPointer(m_state.accumulatorIn,
+ + u", "_qs + contentPointer(m_state.accumulatorIn(),
use(m_state.accumulatorVariableIn))
- + u", "_qs + contentPointer(m_state.accumulatorOut, m_state.accumulatorVariableOut)
+ + 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')';
+ + 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);
+ m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
generateLookup(lookup, initialization, preparation);
- m_body += u"}\n"_qs;
}
}
@@ -1037,10 +1137,10 @@ void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
QString QQmlJSCodeGenerator::setLookupPreparation(
const QQmlJSRegisterContent &content, const QString &arg, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ 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)";
@@ -1058,9 +1158,8 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
m_body.setWriteRegister(QString());
const QString indexString = QString::number(index);
- const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn.storedType();
+ const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
const QQmlJSRegisterContent callBase = registerType(baseReg);
- const QQmlJSScope::ConstPtr objectType = callBase.storedType();
const QQmlJSRegisterContent specific = m_typeResolver->memberType(
callBase, m_jsUnitGenerator->lookupName(index));
const QQmlJSRegisterContent property = specific.storedIn(
@@ -1072,9 +1171,10 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
QString variableInType;
QString preparation;
QString argType;
- if (property != m_state.accumulatorIn) {
+ 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))
+ + conversion(m_state.accumulatorIn(), property, use(m_state.accumulatorVariableIn))
+ u";\n"_qs;
variableIn = contentPointer(property, u"converted"_qs);
variableInType = contentType(property, u"converted"_qs);
@@ -1089,7 +1189,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
argType = variableInType;
}
- switch (objectType->accessSemantics()) {
+ switch (callBase.storedType()->accessSemantics()) {
case QQmlJSScope::AccessSemantics::Reference: {
const QString lookup = u"aotContext->setObjectLookup("_qs + indexString
+ u", "_qs + object + u", "_qs + variableIn + u')';
@@ -1105,7 +1205,7 @@ void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
break;
}
- if (objectType != m_typeResolver->listPropertyType()) {
+ if (!m_typeResolver->registerIsStoredIn(callBase, m_typeResolver->listPropertyType())) {
reject(u"SetLookup on sequence types (because of missing write-back)"_qs);
break;
}
@@ -1181,12 +1281,14 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
QString args;
QString conversions;
- if (m_typeResolver->containedType(m_state.accumulatorOut) == m_typeResolver->voidType()) {
+ 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();
+ const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
m_body += outType->internalName();
if (outType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
m_body += u" *"_qs;
@@ -1194,25 +1296,25 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
m_body += u' ';
m_body += *outVar + u";\n";
- types = metaTypeFromType(m_state.accumulatorOut.storedType());
+ types = metaTypeFromType(m_state.accumulatorOut().storedType());
args = u'&' + *outVar;
}
for (int i = 0; i < argc; ++i) {
- const QQmlJSScope::ConstPtr type = registerType(argv + i).storedType();
+ const QQmlJSRegisterContent content = registerType(argv + i);
const QString var = use(registerVariable(argv + i));
- if (type == m_typeResolver->jsPrimitiveType()) {
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
QString argName = u"arg"_qs + QString::number(i);
conversions += u"QVariant "_qs + argName + u" = "_qs
- + conversion(type, m_typeResolver->varType(), var) + u";\n"_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 (type == m_typeResolver->varType()) {
+ } 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(type);
+ types += u", "_qs + metaTypeFromType(content.storedType());
}
}
return conversions
@@ -1222,17 +1324,13 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
{
- // Generate a new section to set m_state.accumulatorVariableOut,
- // so that m_state.accumulatorVariableOut can be optimized away.
+ if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
+ return;
nextSection();
m_body.setWriteRegister(m_state.accumulatorVariableOut);
m_body += m_state.accumulatorVariableOut + u" = "_qs;
-
- if (outVar.isEmpty())
- m_body += u"{};\n"_qs;
- else
- m_body += u"std::move(" + outVar + u");\n";
+ m_body += u"std::move(" + outVar + u");\n";
nextSection();
m_body.setHasSideEffects(true);
@@ -1272,8 +1370,10 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
addInclude(u"qalgorithms.h"_qs);
addInclude(u"qrandom.h"_qs);
- if (m_state.accumulatorOut.storedType() != m_typeResolver->realType())
- return false;
+ // 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) {
@@ -1287,101 +1387,106 @@ bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int ar
const QString inf = u"std::numeric_limits<double>::infinity()"_qs;
m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ QString expression;
+
if (name == u"abs" && argc == 1) {
- m_body += u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
+ expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_qs;
} else if (name == u"acos"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_qs.arg(qNaN);
} else if (name == u"acosh"_qs && argc == 1) {
- m_body += u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_qs.arg(qNaN);
} else if (name == u"asin"_qs && argc == 1) {
- m_body += u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_qs.arg(qNaN);
} else if (name == u"asinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_qs;
} else if (name == u"atan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_qs;
} else if (name == u"atanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_qs;
+ 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) {
- m_body += u"std::cbrt(arg1)"_qs;
+ expression = u"std::cbrt(arg1)"_qs;
} else if (name == u"ceil"_qs && argc == 1) {
- m_body += u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_qs;
+ 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) {
- m_body += u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
+ expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_qs;
} else if (name == u"cos"_qs && argc == 1) {
- m_body += u"std::cos(arg1)"_qs;
+ expression = u"std::cos(arg1)"_qs;
} else if (name == u"cosh"_qs && argc == 1) {
- m_body += u"std::cosh(arg1)"_qs;
+ expression = u"std::cosh(arg1)"_qs;
} else if (name == u"exp"_qs && argc == 1) {
- m_body += u"std::isinf(arg1) "
+ 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) {
- m_body += u"std::floor(arg1)"_qs;
+ expression = u"std::floor(arg1)"_qs;
} else if (name == u"fround"_qs && argc == 1) {
- m_body += u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
+ 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) {
- m_body += u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
+ expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
"* quint32(QJSNumberCoercion::toInteger(arg2)))"_qs;
} else if (name == u"log"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_qs.arg(qNaN);
} else if (name == u"log10"_qs && argc == 1) {
- m_body += u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_qs.arg(qNaN);
} else if (name == u"log1p"_qs && argc == 1) {
- m_body += u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_qs.arg(qNaN);
} else if (name == u"log2"_qs && argc == 1) {
- m_body += u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
+ expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_qs.arg(qNaN);
} else if (name == u"max"_qs && argc == 2) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) "
+ 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) {
- m_body += u"(qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) "
+ 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) {
- m_body += u"QRandomGenerator::global()->generateDouble()"_qs;
+ expression = u"QRandomGenerator::global()->generateDouble()"_qs;
} else if (name == u"round"_qs && argc == 1) {
- m_body += u"std::isfinite(arg1) "
+ 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) {
- m_body += u"std::isnan(arg1) "
+ 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) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_qs;
} else if (name == u"sinh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_qs;
} else if (name == u"sqrt"_qs && argc == 1) {
- m_body += u"std::sqrt(arg1)"_qs;
+ expression = u"std::sqrt(arg1)"_qs;
} else if (name == u"tan"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_qs;
} else if (name == u"tanh"_qs && argc == 1) {
- m_body += u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
+ expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_qs;
} else if (name == u"trunc"_qs && argc == 1) {
- m_body += u"std::trunc(arg1)"_qs;
+ 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;
@@ -1391,11 +1496,16 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
{
INJECT_TRACE_INFO(generate_CallPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
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->containedType(baseType) == mathObject()) {
+ if (m_typeResolver->equals(m_typeResolver->originalContainedType(baseType), mathObject())) {
if (inlineMathMethod(name, argc, argv))
return;
}
@@ -1458,6 +1568,11 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
{
INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
+ if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
+ reject(u"call to untyped JavaScript function"_qs);
+
+ AccumulatorConverter registers(this);
+
m_body.setHasSideEffects(true);
const QString indexString = QString::number(index);
@@ -1541,7 +1656,7 @@ void QQmlJSCodeGenerator::generate_ThrowException()
generateSetInstructionPointer();
m_body += u"aotContext->engine->throwError("_qs
- + conversion(m_state.accumulatorIn, m_typeResolver->globalType(
+ + 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;
@@ -1674,9 +1789,15 @@ void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
{
- Q_UNUSED(argc);
Q_UNUSED(args);
- reject(u"DefineArray"_qs);
+ if (argc > 0)
+ reject(u"DefineArray"_qs);
+
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->emptyListType(), m_state.accumulatorOut().storedType(),
+ QString());
+ m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->emptyListType());
}
void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
@@ -1745,7 +1866,7 @@ void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
m_body.setWriteRegister(QString());
m_body += u"if ("_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1760,7 +1881,7 @@ void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
m_body.setWriteRegister(QString());
m_body += u"if (!"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
+ m_body += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
use(m_state.accumulatorVariableIn));
m_body += u") "_qs;
generateJumpCodeWithTypeConversions(offset, JumpMode::Conditional);
@@ -1799,42 +1920,47 @@ 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 += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
-
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
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 += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!QJSPrimitiveValue(QJSPrimitiveNull()).equals("_qs
+ + conversion(
+ m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
+ use(m_state.accumulatorVariableIn)) + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
{
- if (m_state.accumulatorIn.storedType() == m_typeResolver->intType())
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->intType()))
return QString::number(lhsConst) + u" == "_qs + use(m_state.accumulatorVariableIn);
- if (m_state.accumulatorIn.storedType() == m_typeResolver->boolType()) {
+ if (m_typeResolver->registerIsStoredIn(m_state.accumulatorIn(), m_typeResolver->boolType())) {
return QString::number(lhsConst) + u" == "_qs
- + conversion(m_state.accumulatorIn.storedType(), m_typeResolver->intType(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->intType(),
use(m_state.accumulatorVariableIn));
}
- if (m_typeResolver->isNumeric(m_state.accumulatorIn)) {
+ 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(),
+ + conversion(m_state.accumulatorIn().storedType(), m_typeResolver->realType(),
use(m_state.accumulatorVariableIn));
}
@@ -1842,7 +1968,7 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
result += conversion(m_typeResolver->intType(), m_typeResolver->jsPrimitiveType(),
QString::number(lhsConst));
result += u".equals("_qs;
- result += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ result += conversion(m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
use(m_state.accumulatorVariableIn));
result += u')';
return result;
@@ -1851,10 +1977,10 @@ QString QQmlJSCodeGenerator::eqIntExpression(int lhsConst)
QString QQmlJSCodeGenerator::getLookupPreparation(
const QQmlJSRegisterContent &content, const QString &var, int lookup)
{
- const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored) {
+ if (m_typeResolver->registerContains(content, content.storedType()))
return QString();
- } else if (stored == m_typeResolver->varType()) {
+
+ if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType())) {
return var + u" = QVariant(aotContext->lookupResultMetaType("_qs
+ QString::number(lookup) + u"))"_qs;
}
@@ -1865,9 +1991,9 @@ QString QQmlJSCodeGenerator::getLookupPreparation(
QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
{
const QQmlJSScope::ConstPtr stored = content.storedType();
- if (m_typeResolver->containedType(content) == stored)
+ if (m_typeResolver->registerContains(content, stored))
return u'&' + var;
- else if (stored == m_typeResolver->varType())
+ else if (m_typeResolver->registerIsStoredIn(content, m_typeResolver->varType()))
return var + u".data()"_qs;
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
return u'&' + var;
@@ -1881,9 +2007,9 @@ QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, c
const QQmlJSScope::ConstPtr stored = content.storedType();
const QQmlJSScope::ConstPtr contained = QQmlJSScope::nonCompositeBaseType(
m_typeResolver->containedType(content));
- if (contained == stored)
+ if (m_typeResolver->equals(contained, stored))
return metaTypeFromType(stored);
- else if (stored == m_typeResolver->varType())
+ 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);
@@ -1896,16 +2022,20 @@ void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpEqInt);
- m_body += m_state.accumulatorVariableOut + u" = "_qs + eqIntExpression(lhsConst)
- + u";\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ eqIntExpression(lhsConst)) + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
{
INJECT_TRACE_INFO(generate_CmpNeInt);
- m_body += m_state.accumulatorVariableOut + u" = !("_qs + eqIntExpression(lhsConst)
- + u");\n"_qs;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ u"!("_qs + eqIntExpression(lhsConst) + u')') + u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
@@ -1973,17 +2103,19 @@ 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.accumulatorOut);
+ const QQmlJSScope::ConstPtr contained
+ = m_typeResolver->containedType(m_state.readRegister(lhs));
m_body += m_state.accumulatorVariableOut + u" = "_qs;
- if (m_state.accumulatorIn.storedType() == m_typeResolver->metaObjectType()
+ if (m_typeResolver->equals(
+ m_state.accumulatorIn().storedType(), m_typeResolver->metaObjectType())
&& contained->isComposite()) {
m_body += conversion(
- m_typeResolver->genericType(contained), m_state.accumulatorOut.storedType(),
+ 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(),
+ m_typeResolver->genericType(contained), m_state.accumulatorOut().storedType(),
u'(' + metaObject(contained) + u")->cast("_qs + input + u')');
}
m_body += u";\n"_qs;
@@ -1992,31 +2124,19 @@ void QQmlJSCodeGenerator::generate_As(int lhs)
void QQmlJSCodeGenerator::generate_UNot()
{
INJECT_TRACE_INFO(generate_UNot);
- m_body += m_state.accumulatorVariableOut;
- m_body += u" = !"_qs;
- m_body += conversion(m_state.accumulatorIn.storedType(), m_typeResolver->boolType(),
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"!"_qs);
}
void QQmlJSCodeGenerator::generate_UPlus()
{
INJECT_TRACE_INFO(generate_UPlus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= +"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"+"_qs);
}
void QQmlJSCodeGenerator::generate_UMinus()
{
INJECT_TRACE_INFO(generate_UMinus);
- m_body += m_state.accumulatorVariableOut;
- m_body += u"= -"_qs;
- m_body += conversion(m_state.accumulatorIn, m_state.accumulatorOut,
- use(m_state.accumulatorVariableIn));
- m_body += u";\n"_qs;
+ generateUnaryOperation(u"-"_qs);
}
void QQmlJSCodeGenerator::generate_UCompl()
@@ -2027,25 +2147,13 @@ void QQmlJSCodeGenerator::generate_UCompl()
void QQmlJSCodeGenerator::generate_Increment()
{
INJECT_TRACE_INFO(generate_Increment);
- 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"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"++"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"++"_qs);
}
void QQmlJSCodeGenerator::generate_Decrement()
{
INJECT_TRACE_INFO(generate_Decrement);
- 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"; "_qs;
- // No line break, to allow removal of the whole thing
- }
- m_body += u"--"_qs + use(m_state.accumulatorVariableOut) + u";\n"_qs;
+ generateInPlaceOperation(u"--"_qs);
}
void QQmlJSCodeGenerator::generate_Add(int lhs)
@@ -2152,16 +2260,18 @@ void QQmlJSCodeGenerator::generate_Mod(int lhs)
registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
use(registerVariable(lhs)));
const auto rhsVar = conversion(
- m_state.accumulatorIn.storedType(), m_typeResolver->jsPrimitiveType(),
+ 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(),
+ m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut().storedType(),
u'(' + lhsVar + u" % "_qs + rhsVar + u')');
m_body += u";\n"_qs;
+
+ generateOutputVariantConversion(m_typeResolver->jsPrimitiveType());
}
void QQmlJSCodeGenerator::generate_Sub(int lhs)
@@ -2217,9 +2327,15 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
QV4::Moth::Instr::Type type)
{
m_state.State::operator=(nextStateFromAnnotations(m_state, *m_annotations));
- m_state.accumulatorVariableIn = m_registerVariables.value(Accumulator)
- .value(m_state.accumulatorIn.storedType());
- Q_ASSERT(!m_state.accumulatorIn.isValid() || !m_state.accumulatorVariableIn.isEmpty());
+ 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()) {
@@ -2234,38 +2350,38 @@ QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
nextSection();
- m_state.accumulatorVariableOut = registerVariable(QQmlJSTypePropagator::Accumulator);
+ 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.
- // If the stored type is void, we don't need a variable, but we want to transport the type
- // information for any enum access or similar.
- Q_ASSERT((m_state.accumulatorOut.isValid()
- && m_state.accumulatorOut.storedType() != m_typeResolver->voidType())
+ Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
|| m_state.accumulatorVariableOut.isEmpty());
- Q_ASSERT(!m_state.accumulatorOut.isValid()
- || m_state.accumulatorOut.storedType() == m_typeResolver->voidType()
- || !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);
- if (nextLine == currentLine + 1 || nextLine < 1) {
+ for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
m_body += u"// "_qs;
- m_body += m_sourceCodeLines.value(currentLine - 1).trimmed();
+ m_body += m_sourceCodeLines.value(line).trimmed();
m_body += u'\n';
- } else {
- m_body += u"/*\n"_qs;
- for (auto line = currentLine - 1; line < nextLine - 1; ++line) {
- m_body += m_sourceCodeLines.at(line).trimmed();
- m_body += u'\n';
- }
- m_body += u"*/\n"_qs;
}
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() && changedRegisterVariable().isEmpty())
+ return SkipInstruction;
+
return ProcessInstruction;
}
@@ -2292,79 +2408,134 @@ void QQmlJSCodeGenerator::generateEqualityOperation(int lhs, const QString &func
const QQmlJSRegisterContent lhsContent = registerType(lhs);
auto isComparable = [&]() {
if (m_typeResolver->isPrimitive(lhsContent)
- && m_typeResolver->isPrimitive(m_state.accumulatorIn)) {
+ && m_typeResolver->isPrimitive(m_state.accumulatorIn())) {
return true;
}
- if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn.isEnumeration())
+ if (m_typeResolver->isNumeric(lhsContent) && m_state.accumulatorIn().isEnumeration())
return true;
- if (m_typeResolver->isNumeric(m_state.accumulatorIn) && lhsContent.isEnumeration())
+ if (m_typeResolver->isNumeric(m_state.accumulatorIn()) && lhsContent.isEnumeration())
return true;
return false;
};
- if (!isComparable())
- reject(u"equality comparison on non-primitive types"_qs);
+ 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();
+ const QQmlJSScope::ConstPtr rhsType = m_state.accumulatorIn().storedType();
m_body += m_state.accumulatorVariableOut + u" = "_qs;
const auto primitive = m_typeResolver->jsPrimitiveType();
- if (lhsType == rhsType && lhsType != primitive) {
- m_body += use(registerVariable(lhs));
- m_body += (invert ? u" != "_qs : u" == "_qs);
- m_body += use(m_state.accumulatorVariableIn);
+ if (m_typeResolver->equals(lhsType, rhsType) && !m_typeResolver->equals(lhsType, primitive)) {
+ m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ use(registerVariable(lhs)) + (invert ? u" != "_qs : u" == "_qs)
+ + 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 += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ (invert ? u"!"_qs : QString())
+ + conversion(registerType(lhs).storedType(), primitive,
+ use(registerVariable(lhs)))
+ + u'.' + function + u'(' + conversion(
+ m_state.accumulatorIn().storedType(), primitive,
+ use(m_state.accumulatorVariableIn))
+ + u')');
}
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
{
- m_body += m_state.accumulatorVariableOut;
+ m_body += m_state.accumulatorVariableOut + u" = "_qs;
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->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 += conversion(
+ m_typeResolver->boolType(), m_state.accumulatorOut().storedType(),
+ conversion(registerType(lhs).storedType(), compareType, use(registerVariable(lhs)))
+ + u' ' + cppOperator + u' '
+ + conversion(m_state.accumulatorIn().storedType(), compareType,
+ use(m_state.accumulatorVariableIn)));
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->boolType());
}
void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
{
- const auto lhsVar = conversion(registerType(lhs), m_state.accumulatorOut,
+ const auto lhsVar = conversion(registerType(lhs), m_state.readRegister(lhs),
use(registerVariable(lhs)));
- const auto rhsVar = conversion(m_state.accumulatorIn, m_state.accumulatorOut,
+ const auto rhsVar = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
use(m_state.accumulatorVariableIn));
Q_ASSERT(!lhsVar.isEmpty());
Q_ASSERT(!rhsVar.isEmpty());
+ const QQmlJSRegisterContent originalOut = m_typeResolver->original(m_state.accumulatorOut());
m_body += m_state.accumulatorVariableOut;
m_body += u" = "_qs;
- m_body += lhsVar;
- m_body += u' ';
- m_body += cppOperator;
- m_body += u' ';
- m_body += rhsVar;
+ m_body += conversion(
+ originalOut, m_state.accumulatorOut(),
+ u'(' + lhsVar + u' ' + cppOperator + u' ' + rhsVar + u')');
m_body += u";\n"_qs;
+ generateOutputVariantConversion(m_typeResolver->containedType(originalOut));
+}
+
+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;
+
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
+}
+
+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;
+ generateOutputVariantConversion(m_typeResolver->containedType(original));
}
void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
@@ -2381,19 +2552,6 @@ void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &i
m_body += u"}\n"_qs;
}
-void QQmlJSCodeGenerator::protectAccumulator()
-{
- // 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.
- if (m_state.accumulatorVariableIn == m_state.accumulatorVariableOut
- && m_state.accumulatorOut.storedType() == m_typeResolver->varType()) {
- m_state.accumulatorVariableIn = use(m_state.accumulatorVariableIn) + u"_moved"_qs;
- m_body += u"QVariant "_qs + m_state.accumulatorVariableIn
- + u" = std::move("_qs + m_state.accumulatorVariableOut + u");\n"_qs;
- }
-}
-
void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset, JumpMode mode)
{
@@ -2402,32 +2560,47 @@ void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
m_body += u"{\n"_qs;
int absoluteOffset =nextInstructionOffset() + relativeOffset;
- const auto annotation = m_annotations->constFind(absoluteOffset);
+ const auto annotation = m_annotations->find(absoluteOffset);
if (annotation != m_annotations->constEnd()) {
- const auto &currentTypes = (*m_annotations)[currentInstructionOffset()].registers;
- const auto &conversions = annotation->expectedTargetTypesBeforeJump;
+ 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() || !currentTypes.contains(registerIndex))
+ if (!targetType.isValid())
continue;
- const QQmlJSRegisterContent currentType = currentTypes.value(registerIndex);
- if (!currentType.isValid() || currentType == targetType)
+
+ 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 &currentRegisterVariables = m_registerVariables[registerIndex];
const auto variable = currentRegisterVariables.constFind(targetType.storedType());
- const QString oldVar = registerVariable(registerIndex);
- if (variable == currentRegisterVariables.end() || *variable == oldVar)
+ if (variable == currentRegisterVariables.end() || *variable == currentVariable)
continue;
nextSection();
m_body.setWriteRegister(*variable);
m_body += *variable;
m_body += u" = "_qs;
- m_body += conversion(currentTypes.value(registerIndex), targetType, use(oldVar));
+ m_body += conversion(currentType, targetType, use(currentVariable));
m_body += u";\n"_qs;
}
}
@@ -2449,117 +2622,210 @@ 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])
+ + castTargetName(m_function->argumentTypes[argumentIndex].storedType())
+ 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]);
- }
+ if (index >= QV4::CallData::OffsetCount && index < firstRegisterIndex())
+ return 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) const
+ 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.
- if (from == to)
+ 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<std::nullptr_t>(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 (base == to)
+ 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 (base == from)
+ if (m_typeResolver->equals(base, from))
return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
}
- } else if (to == m_typeResolver->boolType()) {
+ } else if (m_typeResolver->equals(to, m_typeResolver->boolType())) {
return u'(' + variable + u" != nullptr)"_qs;
}
}
- if (from == m_typeResolver->nullType()
- && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
- return u"static_cast<"_qs + to->internalName() + u" *>("_qs + variable + u')';
- }
-
- const auto jsValueType = m_typeResolver->jsValueType();
-
auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
- return candidate == jsValueType || candidate->isScript();
+ return m_typeResolver->equals(candidate, jsValueType) || candidate->isScript();
};
if (isJsValue(from) && isJsValue(to))
return variable;
- const auto boolType = m_typeResolver->boolType();
const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
return m_typeResolver->isNumeric(m_typeResolver->globalType(type))
- || type == m_typeResolver->boolType()
+ || m_typeResolver->equals(type, m_typeResolver->boolType())
|| type->scopeType() == QQmlJSScope::EnumScope;
};
- if (from == m_typeResolver->realType() && to == m_typeResolver->intType())
+ 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')';
- const auto varType = m_typeResolver->varType();
- const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
- if (from == jsPrimitiveType) {
- if (to == m_typeResolver->realType())
+
+ if (m_typeResolver->equals(from, jsPrimitiveType)) {
+ if (m_typeResolver->equals(to, m_typeResolver->realType()))
return variable + u".toDouble()"_qs;
- if (to == boolType)
+ if (m_typeResolver->equals(to, boolType))
return variable + u".toBoolean()"_qs;
- if (to == m_typeResolver->intType())
+ if (m_typeResolver->equals(to, m_typeResolver->intType()))
return variable + u".toInteger()"_qs;
- if (to == m_typeResolver->stringType())
+ if (m_typeResolver->equals(to, m_typeResolver->stringType()))
return variable + u".toString()"_qs;
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"QJSValue(QJSPrimitiveValue("_qs + variable + u"))"_qs;
- if (to == varType)
+ 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 (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return variable + u".toPrimitive()"_qs;
- if (to == varType)
+ 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 (to == jsPrimitiveType)
+ if (m_typeResolver->equals(to, jsPrimitiveType))
return u"QJSPrimitiveValue("_qs + variable + u')';
- if (to == jsValueType)
+ if (m_typeResolver->equals(to, jsValueType))
return u"aotContext->engine->toScriptValue("_qs + variable + u')';
- if (from == varType) {
- if (to == m_typeResolver->listPropertyType())
+ 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"qvariant_cast<"_qs + castTargetName(to) + u">("_qs + variable + u')';
+ return u"aotContext->engine->fromVariant<"_qs + castTargetName(to) + u">("_qs
+ + variable + u')';
}
- if (to == varType)
+ 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)
@@ -2586,4 +2852,58 @@ 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)
+{
+ if (accumulatorVariableOut.isEmpty())
+ return;
+
+ 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;
+ const auto contained = generator->m_typeResolver->containedType(
+ generator->m_state.accumulatorOut());
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.setRegister(Accumulator, accumulatorOut);
+ generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
+ generator->generateOutputVariantConversion(contained);
+ } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
+ generator->m_body += u"}\n"_qs;
+ generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
+ }
+}
+
+
QT_END_NAMESPACE