11#include <private/qqmljstypepropagator_p.h>
13#include <private/qqmlirbuilder_p.h>
14#include <private/qqmljsscope_p.h>
15#include <private/qqmljsutils_p.h>
16#include <private/qv4compilerscanfunctions_p.h>
17#include <private/qduplicatetracker_p.h>
19#include <QtCore/qdir.h>
20#include <QtCore/qfileinfo.h>
24using namespace Qt::StringLiterals;
27
28
29
30
31
32
33
35#define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
37#define INJECT_TRACE_INFO(function)
38 static const bool injectTraceInfo = true;
39 if (injectTraceInfo) {
40 m_body += u"// "_s
+ QStringLiteral(#function) + u'\n'
;
46static bool isTypeStorable(
const QQmlJSTypeResolver *resolver,
const QQmlJSScope::ConstPtr &type)
48 return !type.isNull() && type != resolver->nullType() && type != resolver->voidType();
51QString QQmlJSCodeGenerator::castTargetName(
const QQmlJSScope::ConstPtr &type)
const
53 return type->augmentedInternalName();
56QQmlJSCodeGenerator::QQmlJSCodeGenerator(
57 const QV4::Compiler::Context *compilerContext,
58 const QV4::Compiler::JSUnitGenerator *unitGenerator,
const QQmlJSTypeResolver *typeResolver,
59 QQmlJSLogger *logger,
const BasicBlocks &basicBlocks,
60 const InstructionAnnotations &annotations)
61 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
62 , m_context(compilerContext)
65QString QQmlJSCodeGenerator::metaTypeFromType(
const QQmlJSScope::ConstPtr &type)
const
67 return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
70QString QQmlJSCodeGenerator::metaTypeFromName(
const QQmlJSScope::ConstPtr &type)
const
72 return u"[]() { static const auto t = QMetaType::fromName(\""_s
73 + QString::fromUtf8(QMetaObject::normalizedType(type->augmentedInternalName().toUtf8()))
74 + u"\"); return t; }()"_s;
77QString QQmlJSCodeGenerator::compositeListMetaType(
const QString &elementName)
const
79 return u"QQmlPrivate::compositeListMetaType(aotContext->compilationUnit, "_s
80 + (m_jsUnitGenerator->hasStringId(elementName)
81 ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
82 : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
85QString QQmlJSCodeGenerator::compositeMetaType(
const QString &elementName)
const
87 return u"QQmlPrivate::compositeMetaType(aotContext->compilationUnit, "_s
88 + (m_jsUnitGenerator->hasStringId(elementName)
89 ? QString::number(m_jsUnitGenerator->getStringId(elementName)) + u')'
90 : u"QStringLiteral(\"%1\"))"_s.arg(elementName));
93QString QQmlJSCodeGenerator::metaObject(
const QQmlJSScope::ConstPtr &objectType)
95 if (objectType->isComposite()) {
96 const QString name = m_typeResolver->nameForType(objectType);
99 u"retrieving the metaObject of a composite type without an element name."_s);
101 return compositeMetaType(name) + u".metaObject()"_s;
104 if (objectType->internalName() == u"QObject"_s
105 || objectType->internalName() == u"QQmlComponent"_s) {
106 return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
108 return metaTypeFromName(objectType) + u".metaObject()"_s;
111QString QQmlJSCodeGenerator::metaType(
const QQmlJSScope::ConstPtr &type)
113 if (type->isComposite()) {
114 const QString name = m_typeResolver->nameForType(type);
115 if (name.isEmpty()) {
117 u"retrieving the metaType of a composite type without an element name."_s);
119 return compositeMetaType(name);
122 if (type->isListProperty() && type->elementType()->isComposite()) {
123 const QString name = m_typeResolver->nameForType(type->elementType());
124 Q_ASSERT(!name.isEmpty());
125 return compositeListMetaType(name);
128 return (m_typeResolver->genericType(type) == type)
129 ? metaTypeFromType(type)
130 : metaTypeFromName(type);
143 return u"s.a%1"_s.arg(registerIndex - QQmlJSCompilePass::Argc);
144 if (registerIndex < 0)
145 return u"s.c%1_%2"_s.arg(-registerIndex).arg(offset);
146 return u"s.v%1_%2"_s.arg(registerIndex).arg(offset);
149QQmlJSAotFunction QQmlJSCodeGenerator::run(
const Function *function,
bool basicBlocksValidationFailed)
151 m_function = function;
153 if (m_context->contextType == QV4::Compiler::ContextType::Binding
154 && m_function->returnType.contains(m_typeResolver->qQmlScriptStringType())) {
155 const QString reason = u"binding for property of type QQmlScriptString; nothing to do."_s;
157 QQmlJSAotFunction result;
158 result.skipReason = reason;
162 const auto addVariable = [&](
int registerIndex,
int offset, QQmlJSRegisterContent content) {
163 const QQmlJSScope::ConstPtr seenType = content.storedType();
165 if (!isTypeStorable(m_typeResolver, seenType))
168 const auto oldSize = m_registerVariables.size();
169 auto &e = m_registerVariables[content];
170 if (m_registerVariables.size() != oldSize) {
171 e.variableName = registerName(registerIndex, offset);
172 e.storedType = seenType;
173 e.initialRegisterIndex = std::abs(registerIndex);
179QT_WARNING_DISABLE_CLANG(
"-Wrange-loop-analysis")
180 for (
auto it = m_annotations.constBegin(), iend = m_annotations.constEnd(); it != iend; ++it) {
181 const int registerIndex = it->second.changedRegisterIndex;
182 if (registerIndex != InvalidRegister)
183 addVariable(registerIndex, it.key(), it->second.changedRegister);
184 for (
auto jt = it->second.typeConversions.constBegin(),
185 jend = it->second.typeConversions.constEnd();
188 const int registerIndex = jt.key();
189 if (registerIndex != InvalidRegister)
190 addVariable(-registerIndex, it.key(), jt.value().content);
196 for (
const auto loopLabel : m_context->labelInfo)
197 m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
202 m_state.State::operator=(initialState(m_function));
204 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Temporary);
205 const QByteArray byteCode = function->code;
206 decode(byteCode.constData(),
static_cast<uint>(byteCode.size()));
207 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Permanent);
209 QQmlJSAotFunction result;
210 result.includes.swap(m_includes);
212 if (basicBlocksValidationFailed) {
213 result.code +=
"// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
214 "with basic blocks validation but compiled without it.\n"_L1;
217 result.code += u"// %1 at line %2, column %3\n"_s
218 .arg(m_context->name).arg(m_context->line).arg(m_context->column);
220 QStringList initializations;
221 QStringList markings;
222 for (
auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
223 registerIt != registerEnd; ++registerIt) {
226 QString declarationName = registerIt->variableName.mid(2);
228 const int registerIndex = registerIt->initialRegisterIndex;
229 const bool registerIsArgument = isArgument(registerIndex);
231 QString code = registerIt->storedType->internalName();
233 const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
235 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
241 if (!registerIsArgument
242 && registerIndex != Accumulator
243 && registerIndex != This
244 && !function->registerTypes[registerIndex - firstRegisterIndex()].contains(
245 m_typeResolver->voidType())) {
246 code += declarationName + u" = "_s;
247 code += convertStored(m_typeResolver->voidType(), storedType, QString());
248 }
else if (registerIsArgument && argumentType(registerIndex).isStoredIn(storedType)) {
249 const int argumentIndex = registerIndex - FirstArgument;
250 const QQmlJSRegisterContent argument
251 = m_function->argumentTypes[argumentIndex];
252 const QQmlJSRegisterContent originalArgument = originalType(argument);
254 const bool needsConversion = argument != originalArgument;
255 if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
265 code += declarationName + u" = "_s;
267 const auto originalContained = m_typeResolver->originalContainedType(argument);
268 QString originalValue;
269 const bool needsQVariantWrapping =
270 storedType->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
271 && !originalContained->isReferenceType()
272 && storedType == m_typeResolver->varType()
273 && originalContained != m_typeResolver->varType();
274 if (needsQVariantWrapping) {
275 originalValue = u"QVariant(%1, argv[%2])"_s.arg(metaTypeFromName(originalContained))
276 .arg(QString::number(argumentIndex + 1));
278 originalValue = u"(*static_cast<"_s + castTargetName(originalArgument.storedType())
279 + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
283 code += conversion(originalArgument, argument, originalValue);
285 code += originalValue;
286 }
else if (isPointer) {
287 code += declarationName + u" = nullptr"_s;
289 code += declarationName;
293 initializations.push_back(std::move(code));
296 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
297 }
else if (storedType == m_typeResolver->varType()) {
298 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
299 }
else if (storedType == m_typeResolver->listPropertyType()) {
301 }
else if (storedType == m_typeResolver->variantMapType()
302 || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
303 QString marking = u" for (const auto &v : std::as_const(" + declarationName + u"))\n"
304 + u" aotContext->mark(v, markStack);\n";
305 markings.append(std::move(marking));
309 result.code += u"struct Storage : QQmlPrivate::AOTTrackedLocalsStorage {\n"_s;
310 result.code += u"Storage(const QQmlPrivate::AOTCompiledContext *ctxt, void **a)"_s;
311 result.code += u" : aotContext(ctxt), argv(a) {}\n"_s;
312 result.code += u"void markObjects(QV4::MarkStack *markStack) const final {"_s;
313 result.code += u" Q_UNUSED(markStack);\n"_s;
316 for (
const QString &marking : std::as_const(markings))
317 result.code += marking;
319 result.code += u"}\n"_s;
320 result.code += u"const QQmlPrivate::AOTCompiledContext *aotContext;\n"_s;
321 result.code += u"void **argv;\n"_s;
324 initializations.sort();
325 for (
const QString &initialization : std::as_const(initializations))
326 result.code += initialization;
328 result.code += u"};\nStorage s(aotContext, argv);\n"_s;
329 result.code += u"aotContext->setLocals(&s);\n"_s;
331 result.code += m_body;
335 = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { contextUnit };\n"
336 " const auto *aotContext = &c;\n"
337 " Q_UNUSED(aotContext);\n"_s;
339 if (function->returnType.isValid()) {
340 signature += u" argTypes[0] = %1;\n"_s.arg(
341 metaType(function->returnType.containedType()));
343 signature += u" argTypes[0] = QMetaType();\n"_s;
345 result.numArguments = function->argumentTypes.length();
346 for (qsizetype i = 0; i != result.numArguments; ++i) {
347 signature += u" argTypes[%1] = %2;\n"_s.arg(
348 QString::number(i + 1),
349 metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
352 result.signature = std::move(signature);
356void QQmlJSCodeGenerator::generateReturnError()
358 const auto finalizeReturn = qScopeGuard([
this]() { m_body += u"return;\n"_s; });
360 m_body += u"aotContext->setReturnValueUndefined();\n"_s;
361 const auto ret = m_function->returnType;
362 if (!ret.isValid() || ret.contains(m_typeResolver->voidType()))
365 m_body += u"if (argv[0]) {\n"_s;
367 const auto contained = ret.containedType();
368 const auto stored = ret.storedType();
369 if (contained->isReferenceType() && stored->isReferenceType()) {
370 m_body += u" *static_cast<"_s
371 + stored->augmentedInternalName()
372 + u" *>(argv[0]) = nullptr;\n"_s;
373 }
else if (contained == stored) {
374 m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
375 + stored->internalName() + u"();\n"_s;
377 m_body += u" const QMetaType returnType = "_s
378 + metaType(ret.containedType()) + u";\n"_s;
379 m_body += u" returnType.destruct(argv[0]);\n"_s;
380 m_body += u" returnType.construct(argv[0]);\n "_s;
386void QQmlJSCodeGenerator::generate_Ret()
390 const auto finalizeReturn = qScopeGuard([
this]() {
391 m_body += u"return;\n"_s;
392 m_skipUntilNextLabel =
true;
396 if (!m_function->returnType.isValid())
399 m_body += u"if (argv[0]) {\n"_s;
401 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
402 const QString in = m_state.accumulatorVariableIn;
404 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
407 if (accumulatorIn.isStoredIn(m_typeResolver->voidType()))
408 m_body += signalUndefined;
409 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
410 m_body += u" if (!"_s + in + u".isValid())\n"_s;
411 m_body += u" "_s + signalUndefined;
412 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
413 m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
414 m_body += u" "_s + signalUndefined;
415 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
416 m_body += u" if ("_s + in + u".isUndefined())\n"_s;
417 m_body += u" "_s + signalUndefined;
420 if (m_function->returnType.contains(m_typeResolver->voidType())) {
425 const auto contained = m_function->returnType.containedType();
426 const auto stored = m_function->returnType.storedType();
427 if (contained == stored || (contained->isReferenceType() && stored->isReferenceType())) {
430 m_body += u" *static_cast<"_s
431 + stored->augmentedInternalName()
432 + u" *>(argv[0]) = "_s
434 accumulatorIn, m_function->returnType,
435 m_typeResolver->isTriviallyCopyable(accumulatorIn.storedType())
437 : u"std::move("_s + in + u')')
439 }
else if (accumulatorIn.contains(contained)) {
440 m_body += u" const QMetaType returnType = "_s + contentType(accumulatorIn, in)
442 m_body += u" returnType.destruct(argv[0]);\n"_s;
443 m_body += u" returnType.construct(argv[0], "_s
444 + contentPointer(accumulatorIn, in) + u");\n"_s;
446 m_body += u" const auto converted = "_s
447 + conversion(accumulatorIn, m_function->returnType,
448 consumedAccumulatorVariableIn()) + u";\n"_s;
449 m_body += u" const QMetaType returnType = "_s
450 + contentType(m_function->returnType, u"converted"_s)
452 m_body += u" returnType.destruct(argv[0]);\n"_s;
453 m_body += u" returnType.construct(argv[0], "_s
454 + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
460void QQmlJSCodeGenerator::generate_Debug()
467 if (value >= std::numeric_limits<
int>::min() && value <= std::numeric_limits<
int>::max()) {
470 return QString::number(i);
473 switch (qFpClassify(value)) {
475 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
476 return std::signbit(value) ? (u'-' + inf) : inf;
479 return u"std::numeric_limits<double>::quiet_NaN()"_s;
481 return std::signbit(value) ? u"-0.0"_s : u"0"_s;
486 return QString::number(value,
'f', std::numeric_limits<
double>::max_digits10);
489void QQmlJSCodeGenerator::generate_LoadConst(
int index)
497 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
498 const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst);
499 const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst);
501 m_body += m_state.accumulatorVariableOut + u" = "_s;
502 if (type == m_typeResolver->realType()) {
503 m_body += conversion(
504 type, m_state.accumulatorOut(),
505 toNumericString(value.doubleValue()));
506 }
else if (type == m_typeResolver->int32Type()) {
507 m_body += conversion(
508 type, m_state.accumulatorOut(),
509 QString::number(value.integerValue()));
510 }
else if (type == m_typeResolver->boolType()) {
511 m_body += conversion(
512 type, m_state.accumulatorOut(),
513 value.booleanValue() ? u"true"_s : u"false"_s);
514 }
else if (type == m_typeResolver->voidType()) {
515 m_body += conversion(
516 type, m_state.accumulatorOut(),
518 }
else if (type == m_typeResolver->nullType()) {
519 m_body += conversion(
520 type, m_state.accumulatorOut(),
523 REJECT(u"unsupported constant type"_s);
529void QQmlJSCodeGenerator::generate_LoadZero()
533 m_body += m_state.accumulatorVariableOut;
534 m_body += u" = "_s + conversion(
535 m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
539void QQmlJSCodeGenerator::generate_LoadTrue()
543 m_body += m_state.accumulatorVariableOut;
544 m_body += u" = "_s + conversion(
545 m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
549void QQmlJSCodeGenerator::generate_LoadFalse()
553 m_body += m_state.accumulatorVariableOut;
554 m_body += u" = "_s + conversion(
555 m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
559void QQmlJSCodeGenerator::generate_LoadNull()
563 m_body += m_state.accumulatorVariableOut + u" = "_s;
564 m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
569void QQmlJSCodeGenerator::generate_LoadUndefined()
573 m_body += m_state.accumulatorVariableOut + u" = "_s;
574 m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
579void QQmlJSCodeGenerator::generate_LoadInt(
int value)
583 m_body += m_state.accumulatorVariableOut;
585 m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
586 QString::number(value));
590void QQmlJSCodeGenerator::generate_MoveConst(
int constIndex,
int destTemp)
594 Q_ASSERT(destTemp == m_state.changedRegisterIndex());
596 auto var = changedRegisterVariable();
600 const auto v4Value = QV4::StaticValue::fromReturnedValue(
601 m_jsUnitGenerator->constant(constIndex));
603 const auto changed = m_state.changedRegister();
604 QQmlJSScope::ConstPtr contained;
607 m_body += var + u" = "_s;
608 if (v4Value.isNull()) {
609 contained = m_typeResolver->nullType();
610 }
else if (v4Value.isUndefined()) {
611 contained = m_typeResolver->voidType();
612 }
else if (v4Value.isBoolean()) {
613 contained = m_typeResolver->boolType();
614 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
615 }
else if (v4Value.isInteger()) {
616 contained = m_typeResolver->int32Type();
617 input = QString::number(v4Value.int_32());
618 }
else if (v4Value.isDouble()) {
619 contained = m_typeResolver->realType();
620 input = toNumericString(v4Value.doubleValue());
622 REJECT(u"unknown const type"_s);
624 m_body += conversion(contained, changed, input) + u";\n"_s;
627void QQmlJSCodeGenerator::generate_LoadReg(
int reg)
631 if (registerVariable(reg) == m_state.accumulatorVariableOut)
634 m_body += m_state.accumulatorVariableOut;
636 m_body += conversion(
637 registerType(reg), m_state.accumulatorOut(), consumedRegisterVariable(reg));
641void QQmlJSCodeGenerator::generate_StoreReg(
int reg)
645 Q_ASSERT(m_state.changedRegisterIndex() == reg);
646 Q_ASSERT(m_state.accumulatorIn().isValid());
647 const QString var = changedRegisterVariable();
650 if (var == m_state.accumulatorVariableIn)
654 m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
655 consumedAccumulatorVariableIn());
659void QQmlJSCodeGenerator::generate_MoveReg(
int srcReg,
int destReg)
663 Q_ASSERT(m_state.changedRegisterIndex() == destReg);
664 const QString destRegName = changedRegisterVariable();
665 if (destRegName.isEmpty())
667 if (destRegName == registerVariable(srcReg))
669 m_body += destRegName;
671 m_body += conversion(
672 registerType(srcReg), m_state.changedRegister(), consumedRegisterVariable(srcReg));
676void QQmlJSCodeGenerator::generate_LoadImport(
int index)
682void QQmlJSCodeGenerator::generate_LoadLocal(
int index)
688void QQmlJSCodeGenerator::generate_StoreLocal(
int index)
694void QQmlJSCodeGenerator::generate_LoadScopedLocal(
int scope,
int index)
701void QQmlJSCodeGenerator::generate_StoreScopedLocal(
int scope,
int index)
708void QQmlJSCodeGenerator::generate_LoadRuntimeString(
int stringId)
712 m_body += m_state.accumulatorVariableOut;
714 m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
715 QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
719void QQmlJSCodeGenerator::generate_MoveRegExp(
int regExpId,
int destReg)
726void QQmlJSCodeGenerator::generate_LoadClosure(
int value)
732void QQmlJSCodeGenerator::generate_LoadName(
int nameIndex)
738void QQmlJSCodeGenerator::generate_LoadGlobalLookup(
int index)
742 AccumulatorConverter registers(
this);
744 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index) + u", "_s
745 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
746 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
747 + QString::number(index) + u", "_s
748 + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
749 const QString preparation = getLookupPreparation(
750 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
751 generateLookup(lookup, initialization, preparation);
754void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(
int index)
758 AccumulatorConverter registers(
this);
760 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
761 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
764 m_body += m_state.accumulatorVariableOut + u" = "_s
766 m_typeResolver->jsValueType(), m_state.accumulatorOut(),
767 u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
772 const QString indexString = QString::number(index);
773 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
774 const QString lookup = u"aotContext->loadContextIdLookup("_s
775 + indexString + u", "_s
776 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
777 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
778 + indexString + u')';
779 generateLookup(lookup, initialization);
783 const bool isProperty = m_state.accumulatorOut().isProperty();
784 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
786 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
787 + indexString + u", "_s
788 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
789 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
790 + indexString + u')';
791 const QString preparation = getLookupPreparation(
792 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
794 generateLookup(lookup, initialization, preparation);
795 }
else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
796 generateTypeLookup(index);
798 REJECT(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
802void QQmlJSCodeGenerator::generate_StoreNameSloppy(
int nameIndex)
806 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
807 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
808 Q_ASSERT(type.isProperty());
810 switch (type.variant()) {
811 case QQmlJSRegisterContent::Property: {
814 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
816 + contentPointer(m_state.accumulatorIn(), m_state.accumulatorVariableIn)
818 + contentType(m_state.accumulatorIn(), m_state.accumulatorVariableIn) + u')';
822 case QQmlJSRegisterContent::Method:
823 REJECT(u"assignment to scope method"_s);
829void QQmlJSCodeGenerator::generate_StoreNameStrict(
int name)
835void QQmlJSCodeGenerator::generate_LoadElement(
int base)
839 const QQmlJSRegisterContent baseType = registerType(base);
841 if (!baseType.isList() && !baseType.isStoredIn(m_typeResolver->stringType()))
842 REJECT(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
844 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
845 conversion(literalType(m_typeResolver->voidType()),
846 m_state.accumulatorOut(), QString()) + u";\n"_s;
848 AccumulatorConverter registers(
this);
850 QString indexName = m_state.accumulatorVariableIn;
851 QQmlJSScope::ConstPtr indexType;
852 if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
853 indexType = m_state.accumulatorIn().containedType();
854 }
else if (m_state.accumulatorIn().isConversion()) {
855 const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
856 if (m_typeResolver->isNumeric(target)) {
857 indexType = target.containedType();
858 m_body += u"if (!" + indexName + u".metaType().isValid())\n"
861 indexName = convertStored(
862 m_state.accumulatorIn().storedType(), indexType, indexName);
864 REJECT(u"LoadElement with non-numeric argument"_s);
868 const QString baseName = registerVariable(base);
870 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
871 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
874 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
875 m_body += u"if ("_s + indexName + u" < 0)\n"_s
880 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
882 m_body += u"if ("_s + indexName + u" < "_s + baseName
883 + u".count(&"_s + baseName + u"))\n"_s;
884 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
885 conversion(m_typeResolver->qObjectType(), m_state.accumulatorOut(),
886 baseName + u".at(&"_s + baseName + u", "_s
887 + indexName + u')') + u";\n"_s;
888 m_body += u"else\n"_s
894 QQmlJSRegisterContent elementType = m_typeResolver->elementType(baseType);
895 elementType = m_pool->storedIn(
896 elementType, m_typeResolver->storedType(elementType.containedType()));
898 QString access = baseName + u".at("_s + indexName + u')';
901 if (baseType.isStoredIn(m_typeResolver->stringType()))
902 access = u"QString("_s + access + u")"_s;
903 else if (isRegisterAffectedBySideEffects(base))
904 REJECT(u"LoadElement on a sequence potentially affected by side effects"_s);
905 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
906 REJECT(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
908 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
909 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
910 conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
911 m_body += u"else\n"_s
915void QQmlJSCodeGenerator::generate_StoreElement(
int base,
int index)
919 const QQmlJSRegisterContent baseType = registerType(base);
920 const QQmlJSScope::ConstPtr indexType = registerType(index).containedType();
922 if (!m_typeResolver->isNumeric(indexType) || !baseType.isList())
923 REJECT(u"StoreElement with non-list base type or non-numeric arguments"_s);
925 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
926 REJECT(u"indirect StoreElement"_s);
928 const QString baseName = registerVariable(base);
929 const QString indexName = registerVariable(index);
931 const auto elementType = m_typeResolver->genericType(
932 m_typeResolver->elementType(baseType).containedType());
934 addInclude(u"QtQml/qjslist.h"_s);
935 if (!m_typeResolver->isNativeArrayIndex(indexType))
936 m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
937 else if (!m_typeResolver->isUnsignedInteger(indexType))
938 m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
942 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
943 m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
945 m_body += u" "_s + baseName + u".replace(&"_s + baseName
946 + u", "_s + indexName + u", "_s;
947 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
953 if (isRegisterAffectedBySideEffects(base))
954 REJECT(u"StoreElement on a sequence potentially affected by side effects"_s);
955 if (isRegisterAffectedBySideEffects(Accumulator))
956 REJECT(u"StoreElement of a value potentially affected by side effects"_s);
958 m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
959 m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
960 + indexName + u" + 1);\n"_s;
961 m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
962 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
966 generateWriteBack(base);
969void QQmlJSCodeGenerator::generate_LoadProperty(
int nameIndex)
972 REJECT(u"LoadProperty"_s);
975void QQmlJSCodeGenerator::generate_LoadOptionalProperty(
int name,
int offset)
982void QQmlJSCodeGenerator::generateEnumLookup(
int index)
984 const QString enumMember = m_state.accumulatorOut().enumMember();
991 if (enumMember.isEmpty())
992 REJECT(u"Lookup of enum metatype"_s);
995 const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
996 if (metaEnum.hasValues()) {
997 m_body += m_state.accumulatorVariableOut + u" = "_s
998 + QString::number(metaEnum.value(enumMember));
1003 const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
1006 Q_ASSERT(!scopeType->isComposite());
1008 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
1009 if (enumName.isEmpty()) {
1010 if (metaEnum.isFlag() && !metaEnum.name().isEmpty()) {
1011 REJECT(u"qmltypes misses name entry for flag; "
1012 "did you pass the enum type to Q_FLAG instead of the QFlag type?"
1013 "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
1015 REJECT(u"qmltypes misses name entry for enum"_s);
1017 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
1018 + u", &"_s + m_state.accumulatorVariableOut + u')';
1019 const QString initialization = u"aotContext->initGetEnumLookup("_s
1020 + QString::number(index) + u", "_s + metaObject(scopeType)
1021 + u", \""_s + enumName + u"\", \""_s + enumMember
1023 generateLookup(lookup, initialization);
1026void QQmlJSCodeGenerator::generateTypeLookup(
int index)
1028 const QString indexString = QString::number(index);
1029 const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator).content;
1030 const QString namespaceString
1031 = accumulatorIn.isImportNamespace()
1032 ? QString::number(accumulatorIn.importNamespace())
1033 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1035 switch (m_state.accumulatorOut().variant()) {
1036 case QQmlJSRegisterContent::Singleton: {
1037 rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
1038 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
1039 + u", &"_s + m_state.accumulatorVariableOut + u')';
1040 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
1041 + u", "_s + namespaceString + u')';
1042 generateLookup(lookup, initialization);
1045 case QQmlJSRegisterContent::ModulePrefix:
1047 case QQmlJSRegisterContent::Attachment: {
1048 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1049 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1050 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
1051 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
1052 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
1053 generateLookup(lookup, initialization);
1056 case QQmlJSRegisterContent::Script:
1057 REJECT(u"script lookup"_s);
1058 case QQmlJSRegisterContent::MetaType: {
1059 if (!m_state.accumulatorOut().isStoredIn(m_typeResolver->metaObjectType())) {
1062 REJECT(u"meta-object stored in different type"_s);
1064 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
1065 + u", &"_s + m_state.accumulatorVariableOut + u')';
1066 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
1067 + u", "_s + namespaceString + u")"_s;
1068 generateLookup(lookup, initialization);
1076void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1077 QQmlJSRegisterContent nonStorableContent,
const QString ®isterName,
bool invert)
1079 const auto nonStorableType = nonStorableContent.containedType();
1080 QQmlJSScope::ConstPtr comparedType = (nonStorableType == m_typeResolver->nullType())
1081 ? m_typeResolver->nullType()
1082 : m_typeResolver->voidType();
1085 m_body += u"if ("_s + registerName
1086 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
1087 + m_state.accumulatorVariableOut + u" = "_s
1088 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1089 u"static_cast<const QJSPrimitiveValue *>("_s + registerName
1090 + u".constData())"_s + u"->type() "_s
1091 + (invert ? u"!="_s : u"=="_s)
1092 + (comparedType == m_typeResolver->nullType()
1093 ? u"QJSPrimitiveValue::Null"_s
1094 : u"QJSPrimitiveValue::Undefined"_s))
1095 + u";\n} else if ("_s + registerName
1096 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
1097 + m_state.accumulatorVariableOut + u" = "_s
1098 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1099 (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
1100 + registerName + u".constData())"_s + u"->"_s
1101 + (comparedType == m_typeResolver->nullType()
1103 : u"isUndefined()"_s))
1108 if (nonStorableType == m_typeResolver->nullType()) {
1109 m_body += u"else if ("_s + registerName
1110 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
1111 + m_state.accumulatorVariableOut + u" = "_s
1112 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1113 u"*static_cast<QObject *const *>("_s + registerName
1114 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
1116 + u";\n} else if ("_s + registerName
1117 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
1118 + m_state.accumulatorVariableOut + u" = "_s
1119 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1120 (invert ? u"false"_s : u"true"_s))
1125 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
1126 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1127 (invert ? (registerName + u".isValid() ? true : false"_s)
1128 : (registerName + u".isValid() ? false : true"_s)))
1132void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1133 QQmlJSRegisterContent storableContent,
const QString &typedRegisterName,
1134 const QString &varRegisterName,
bool invert)
1138 const QQmlJSScope::ConstPtr contained = storableContent.isEnumeration()
1139 ? storableContent.storedType()
1140 : storableContent.containedType();
1142 const QQmlJSScope::ConstPtr boolType = m_typeResolver->boolType();
1143 if (contained->isReferenceType()) {
1144 const QQmlJSScope::ConstPtr comparable = m_typeResolver->qObjectType();
1145 const QString cmpExpr = (invert ? u"!"_s : QString()) + u"(("
1146 + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
1147 + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
1148 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1150 m_body += m_state.accumulatorVariableOut + u" = "_s
1151 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n";
1155 if (m_typeResolver->isPrimitive(contained)) {
1156 const QQmlJSScope::ConstPtr comparable = m_typeResolver->jsPrimitiveType();
1157 const QString cmpExpr = (invert ? u"!"_s : QString())
1158 + conversion(storableContent, comparable, typedRegisterName)
1159 + u".strictlyEquals("_s
1160 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1162 m_body += m_state.accumulatorVariableOut + u" = "_s
1163 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n"_s;
1167 REJECT(u"comparison of non-primitive, non-object type to var"_s);
1170void QQmlJSCodeGenerator::generateArrayInitializer(
int argc,
int argv)
1172 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1173 const QQmlJSScope::ConstPtr element = stored->elementType();
1176 QStringList initializer;
1177 for (
int i = 0; i < argc; ++i) {
1178 initializer += convertStored(
1179 registerType(argv + i).storedType(), element,
1180 consumedRegisterVariable(argv + i));
1183 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
1184 m_body += initializer.join(u", "_s);
1188void QQmlJSCodeGenerator::generateWriteBack(
int registerIndex)
1190 QString writeBackRegister = registerVariable(registerIndex);
1191 bool writeBackAffectedBySideEffects = isRegisterAffectedBySideEffects(registerIndex);
1193 for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
1194 !writeBack.storedType()->isReferenceType();) {
1195 if (writeBackAffectedBySideEffects)
1196 REJECT(u"write-back of value affected by side effects"_s);
1198 if (writeBack.isConversion())
1199 REJECT(u"write-back of converted value"_s);
1201 switch (writeBack.variant()) {
1202 case QQmlJSRegisterContent::Literal:
1203 case QQmlJSRegisterContent::Operation:
1211 const int lookupIndex = writeBack.resultLookupIndex();
1222 if (lookupIndex == -1)
1223 REJECT(u"write-back of non-lookup"_s);
1225 const QString writeBackIndexString = QString::number(lookupIndex);
1227 const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
1228 if (variant == QQmlJSRegisterContent::Property && isQmlScopeObject(writeBack.scope())) {
1229 const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
1230 + writeBackIndexString
1231 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1232 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
1233 + writeBackIndexString + u')';
1234 generateLookup(lookup, initialization);
1239 QQmlJSRegisterContent outerContent;
1240 QString outerRegister;
1241 bool outerAffectedBySideEffects =
false;
1242 for (
auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
1244 if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
1245 outerContent = it.value().content;
1246 outerRegister = lookupVariable(outerContent.resultLookupIndex());
1247 outerAffectedBySideEffects = it.value().affectedBySideEffects;
1253 if (!outerContent.isValid())
1254 REJECT(u"write-back of lookup across jumps or merges."_s);
1256 Q_ASSERT(!outerRegister.isEmpty());
1258 switch (writeBack.variant()) {
1259 case QQmlJSRegisterContent::Property:
1260 if (writeBack.scopeType()->isReferenceType()) {
1261 const QString lookup = u"aotContext->writeBackObjectLookup("_s
1262 + writeBackIndexString
1263 + u", "_s + outerRegister
1264 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1266 const QString initialization = (m_state.registers[registerIndex].isShadowable
1267 ? u"aotContext->initGetObjectLookupAsVariant("_s
1268 : u"aotContext->initGetObjectLookup("_s)
1269 + writeBackIndexString + u", "_s + outerRegister + u')';
1271 generateLookup(lookup, initialization);
1273 const QString valuePointer = contentPointer(outerContent, outerRegister);
1274 const QString lookup = u"aotContext->writeBackValueLookup("_s
1275 + writeBackIndexString
1276 + u", "_s + valuePointer
1277 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1278 const QString initialization = u"aotContext->initGetValueLookup("_s
1279 + writeBackIndexString
1280 + u", "_s + metaObject(writeBack.scopeType()) + u')';
1281 generateLookup(lookup, initialization);
1285 REJECT(u"SetLookup on value types (because of missing write-back)"_s);
1288 writeBackRegister = outerRegister;
1289 writeBack = outerContent;
1290 writeBackAffectedBySideEffects = outerAffectedBySideEffects;
1294void QQmlJSCodeGenerator::rejectIfNonQObjectOut(
const QString &error)
1296 if (m_state.accumulatorOut().storedType()->accessSemantics()
1297 != QQmlJSScope::AccessSemantics::Reference) {
1302void QQmlJSCodeGenerator::rejectIfBadArray()
1304 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1305 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
1309 REJECT(u"storing an array in a non-sequence type"_s);
1310 }
else if (stored->isListProperty()) {
1319 REJECT(u"creating a QQmlListProperty not backed by a property"_s);
1325
1326
1327
1328
1329
1330
1331bool QQmlJSCodeGenerator::generateContentPointerCheck(
1332 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1333 const QString &variable,
const QString &errorMessage)
1335 const QQmlJSScope::ConstPtr scope = required;
1336 const QQmlJSScope::ConstPtr input = actual.containedType();
1337 if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
1338 [&](
const QQmlJSScope::ConstPtr &base) {
return base == scope; })) {
1342 if (!m_typeResolver->canHold(input, scope)) {
1343 REJECT<
bool>(u"lookup of members of %1 in %2"_s
1344 .arg(scope->internalName(), input->internalName()));
1347 bool needsVarContentConversion =
false;
1348 QString processedErrorMessage;
1349 if (actual.storedType()->isReferenceType()) {
1353 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
1354 processedErrorMessage = errorMessage.arg(u"null");
1355 }
else if (actual.isStoredIn(m_typeResolver->varType())) {
1359 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
1360 needsVarContentConversion =
true;
1361 processedErrorMessage = errorMessage.arg(u"undefined");
1363 REJECT<
bool>(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
1366 generateSetInstructionPointer();
1367 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
1368 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
1369 generateReturnError();
1371 return needsVarContentConversion;
1374QString QQmlJSCodeGenerator::generateCallConstructor(
1375 const QQmlJSMetaMethod &ctor,
const QList<QQmlJSRegisterContent> &argumentTypes,
1376 const QStringList &arguments,
const QString &metaType,
const QString &metaObject)
1378 const auto parameterTypes = ctor.parameters();
1379 Q_ASSERT(parameterTypes.length() == argumentTypes.length());
1382 QStringList argPointers;
1384 QString result = u"[&](){\n"_s;
1385 for (qsizetype i = 0, end = parameterTypes.length(); i < end; ++i) {
1386 const QQmlJSRegisterContent argumentType = argumentTypes[i];
1387 const QQmlJSScope::ConstPtr parameterType = parameterTypes[i].type();
1388 const QString argument = arguments[i];
1389 const QString arg = u"arg"_s + QString::number(i);
1391 result += u" auto "_s + arg + u" = "_s;
1392 if (argumentType.contains(parameterType)) {
1394 argPointers.append(contentPointer(argumentType, arg));
1396 const QQmlJSRegisterContent parameterTypeConversion
1398 m_typeResolver->convert(argumentType, parameterType),
1399 m_typeResolver->genericType(parameterType));
1400 result += conversion(argumentType, parameterTypeConversion, argument);
1401 argPointers.append(contentPointer(parameterTypeConversion, arg));
1406 result += u" void *args[] = {"_s + argPointers.join(u',') + u"};\n"_s;
1407 result += u" return aotContext->constructValueType("_s + metaType + u", "_s + metaObject
1408 + u", "_s + QString::number(
int(ctor.constructorIndex())) + u", args);\n"_s;
1410 return result + u"}()"_s;
1413bool QQmlJSCodeGenerator::isRegisterAffectedBySideEffects(
int registerIndex)
1415 if (!m_state.isRegisterAffectedBySideEffects(registerIndex))
1418 QQmlJSRegisterContent baseType = registerType(registerIndex);
1419 const QQmlJSScope::ConstPtr contained = baseType.containedType();
1420 switch (contained->accessSemantics()) {
1421 case QQmlSA::AccessSemantics::Reference:
1422 case QQmlSA::AccessSemantics::None:
1425 case QQmlSA::AccessSemantics::Value:
1429 return !m_typeResolver->isPrimitive(contained);
1430 case QQmlSA::AccessSemantics::Sequence: {
1432 if (contained->isListProperty())
1435 switch (baseType.variant()) {
1436 case QQmlJSRegisterContent::Operation:
1437 case QQmlJSRegisterContent::Literal: {
1439 const QQmlJSScope::ConstPtr elementContained = contained->elementType();
1440 return !elementContained->isReferenceType()
1441 && !m_typeResolver->isPrimitive(elementContained);
1451QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
1452 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1453 const QString &variable,
const QString &errorMessage)
1455 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1456 return variable + u".data()"_s;
1457 return contentPointer(actual, variable);
1460QString QQmlJSCodeGenerator::resolveQObjectPointer(
1461 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1462 const QString &variable,
const QString &errorMessage)
1464 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1465 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1469void QQmlJSCodeGenerator::generate_GetLookup(
int index)
1472 generate_GetLookupHelper(index);
1475QString QQmlJSCodeGenerator::generateVariantMapGetLookup(
1476 const QString &map,
const int nameIndex)
1478 const QString mapLookup = map
1479 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1481 return m_state.accumulatorVariableOut + u" = "_s
1482 + conversion(m_typeResolver->varType(), m_state.accumulatorOut(), mapLookup)
1486QString QQmlJSCodeGenerator::generateVariantMapSetLookup(
1487 const QString &map,
const int nameIndex,
1488 const QQmlJSScope::ConstPtr &property,
const QString &variableIn)
1490 const QString mapLookup = map
1491 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1493 return mapLookup + u" = "_s
1494 + conversion(property, m_typeResolver->varType(), variableIn)
1498void QQmlJSCodeGenerator::generate_GetLookupHelper(
int index)
1500 if (m_state.accumulatorOut().isMethod())
1501 REJECT(u"lookup of function property."_s);
1503 if (m_state.accumulatorOut().scope().contains(m_typeResolver->mathObject())) {
1504 QString name = m_jsUnitGenerator->lookupName(index);
1508 value = std::exp(1.0);
1509 }
else if (name == u"LN10") {
1511 }
else if (name == u"LN2") {
1513 }
else if (name == u"LOG10E") {
1514 value = log10(std::exp(1.0));
1515 }
else if (name == u"LOG2E") {
1516 value = log2(std::exp(1.0));
1517 }
else if (name == u"PI") {
1518 value = 3.14159265358979323846;
1519 }
else if (name == u"SQRT1_2") {
1520 value = std::sqrt(0.5);
1521 }
else if (name == u"SQRT2") {
1522 value = std::sqrt(2.0);
1527 m_body += m_state.accumulatorVariableOut + u" = "_s
1528 + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value))
1533 if (m_state.accumulatorOut().isImportNamespace()) {
1534 Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ModulePrefix);
1536 if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
1537 m_body += m_state.accumulatorVariableOut + u" = "_s
1538 + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
1539 m_state.accumulatorVariableIn)
1545 AccumulatorConverter registers(
this);
1547 if (m_state.accumulatorOut().isEnumeration()) {
1548 generateEnumLookup(index);
1552 const QString indexString = QString::number(index);
1553 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1554 ? QString::number(m_state.accumulatorIn().importNamespace())
1555 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1556 const auto accumulatorIn = m_state.accumulatorIn();
1557 const QQmlJSRegisterContent scope = m_state.accumulatorOut().scope();
1558 const bool isReferenceType = scope.containedType()->isReferenceType();
1560 switch (m_state.accumulatorOut().variant()) {
1561 case QQmlJSRegisterContent::Attachment: {
1562 if (isQmlScopeObject(m_state.accumulatorOut().attachee())) {
1563 generateTypeLookup(index);
1566 if (!isReferenceType) {
1571 REJECT(u"attached object for non-QObject type"_s);
1574 if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
1577 REJECT(u"attached object of potentially non-QObject base"_s);
1580 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1582 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1583 + u", "_s + m_state.accumulatorVariableIn
1584 + u", &"_s + m_state.accumulatorVariableOut + u')';
1585 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1586 + indexString + u", "_s + namespaceString + u", "_s
1587 + m_state.accumulatorVariableIn + u')';
1588 generateLookup(lookup, initialization);
1591 case QQmlJSRegisterContent::Singleton:
1592 case QQmlJSRegisterContent::Script:
1593 case QQmlJSRegisterContent::MetaType: {
1594 generateTypeLookup(index);
1601 Q_ASSERT(m_state.accumulatorOut().isProperty());
1603 if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1604 REJECT(u"lookup in QJSValue"_s);
1605 }
else if (isReferenceType) {
1606 const QString inputPointer = resolveQObjectPointer(
1607 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1608 u"Cannot read property '%1' of %2"_s.arg(
1609 m_jsUnitGenerator->lookupName(index)));
1610 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1611 + u", "_s + inputPointer + u", "_s
1612 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
1613 const QString initialization = (m_state.isShadowable()
1614 ? u"aotContext->initGetObjectLookupAsVariant("_s
1615 : u"aotContext->initGetObjectLookup("_s)
1616 + indexString + u", "_s + inputPointer + u')';
1617 const QString preparation = getLookupPreparation(
1618 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1619 generateLookup(lookup, initialization, preparation);
1620 }
else if ((scope.containedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1621 || scope.contains(m_typeResolver->stringType()))
1622 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1623 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1624 if (stored->isListProperty()) {
1625 m_body += m_state.accumulatorVariableOut + u" = "_s;
1626 m_body += conversion(
1627 originalType(m_state.accumulatorOut()),
1628 m_state.accumulatorOut(),
1629 m_state.accumulatorVariableIn + u".count("_s + u'&'
1630 + m_state.accumulatorVariableIn + u')');
1632 }
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1633 || stored == m_typeResolver->stringType()) {
1634 m_body += m_state.accumulatorVariableOut + u" = "_s
1635 + conversion(originalType(m_state.accumulatorOut()),
1636 m_state.accumulatorOut(),
1637 m_state.accumulatorVariableIn + u".length()"_s)
1640 REJECT(u"access to 'length' property of sequence wrapped in non-sequence"_s);
1642 }
else if (accumulatorIn.isStoredIn(m_typeResolver->variantMapType())) {
1643 m_body += generateVariantMapGetLookup(m_state.accumulatorVariableIn, index);
1645 if (m_state.isRegisterAffectedBySideEffects(Accumulator))
1646 REJECT(u"reading from a value that's potentially affected by side effects"_s);
1648 const QString inputContentPointer = resolveValueTypeContentPointer(
1649 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1650 u"Cannot read property '%1' of %2"_s.arg(
1651 m_jsUnitGenerator->lookupName(index)));
1653 if (scope.contains(m_typeResolver->variantMapType())) {
1654 m_body += generateVariantMapGetLookup(
1655 u"(*static_cast<const QVariantMap *>("_s
1656 + inputContentPointer + u"))"_s, index);
1660 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1661 + u", "_s + inputContentPointer
1662 + u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
1664 const QString initialization = u"aotContext->initGetValueLookup("_s
1665 + indexString + u", "_s
1666 + metaObject(scope.containedType()) + u')';
1667 const QString preparation = getLookupPreparation(
1668 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1669 generateLookup(lookup, initialization, preparation);
1673void QQmlJSCodeGenerator::generate_GetOptionalLookup(
int index,
int offset)
1677 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
1678 QString accumulatorVarIn = m_state.accumulatorVariableIn;
1680 const auto &annotation = m_annotations[currentInstructionOffset()];
1681 if (accumulatorIn.storedType()->isReferenceType()) {
1682 m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
1683 generateJumpCodeWithTypeConversions(offset);
1684 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
1685 m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
1686 "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
1687 generateJumpCodeWithTypeConversions(offset);
1688 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
1689 m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
1690 "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
1691 generateJumpCodeWithTypeConversions(offset);
1692 }
else if (annotation.changedRegisterIndex == Accumulator
1693 && annotation.changedRegister.variant() == QQmlJSRegisterContent::Enum) {
1695 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1696 m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
1697 generateJumpCodeWithTypeConversions(offset);
1698 }
else if (!m_typeResolver->canHoldUndefined(accumulatorIn.storage())) {
1704 generate_GetLookupHelper(index);
1707void QQmlJSCodeGenerator::generate_StoreProperty(
int nameIndex,
int baseReg)
1711 REJECT(u"StoreProperty"_s);
1719 Q_ASSERT(contentPointer.startsWith(u'&') || contentPointer[0].isLetterOrNumber());
1720 return contentPointer.startsWith(u'&') ? contentPointer.mid(1) : (u'*' + contentPointer);
1723void QQmlJSCodeGenerator::generate_SetLookup(
int index,
int baseReg)
1727 const QString indexString = QString::number(index);
1728 const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
1729 const QQmlJSRegisterContent property = m_state.readAccumulator();
1730 Q_ASSERT(property.isConversion());
1731 const QQmlJSRegisterContent original
1732 = m_typeResolver->original(property.conversionResultScope());
1733 const QQmlJSScope::ConstPtr originalScope = original.containedType();
1735 if (property.storedType().isNull()) {
1736 REJECT(u"SetLookup. Could not find property "
1737 + m_jsUnitGenerator->lookupName(index)
1739 + originalScope->internalName());
1742 const QString object = registerVariable(baseReg);
1745 if (!m_state.accumulatorIn().contains(property.containedType())) {
1746 m_body += property.storedType()->augmentedInternalName() + u" converted = "_s
1747 + conversion(m_state.accumulatorIn(), property, consumedAccumulatorVariableIn())
1749 variableIn = contentPointer(property, u"converted"_s);
1751 variableIn = contentPointer(property, m_state.accumulatorVariableIn);
1754 switch (originalScope->accessSemantics()) {
1755 case QQmlJSScope::AccessSemantics::Reference: {
1756 const QString basePointer = resolveQObjectPointer(
1757 originalScope, registerType(baseReg), object,
1758 u"TypeError: Value is %1 and could not be converted to an object"_s);
1760 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1761 + u", "_s + basePointer + u", "_s + variableIn + u')';
1765 const QString initialization
1766 = (property.contains(m_typeResolver->varType())
1767 ? u"aotContext->initSetObjectLookupAsVariant("_s
1768 : u"aotContext->initSetObjectLookup("_s)
1769 + indexString + u", "_s + basePointer + u')';
1770 generateLookup(lookup, initialization);
1773 case QQmlJSScope::AccessSemantics::Sequence: {
1774 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1775 if (propertyName != u"length"_s)
1776 REJECT(u"setting non-length property on a sequence type"_s);
1778 if (!originalScope->isListProperty())
1779 REJECT(u"resizing sequence types (because of missing write-back)"_s);
1782 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1783 m_body += u"const int end = "_s + derefContentPointer(variableIn) + u";\n"_s;
1784 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1785 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1786 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1787 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1791 case QQmlJSScope::AccessSemantics::Value: {
1792 const QQmlJSRegisterContent base = registerType(baseReg);
1793 if (base.isStoredIn(m_typeResolver->variantMapType())) {
1794 m_body += generateVariantMapSetLookup(
1795 registerVariable(baseReg), index, property.storedType(),
1796 derefContentPointer(variableIn));
1797 generateWriteBack(baseReg);
1800 const QString baseContentPointer = resolveValueTypeContentPointer(
1801 originalScope, base, object,
1802 u"TypeError: Value is %1 and could not be converted to an object"_s);
1804 if (original.contains(m_typeResolver->variantMapType())) {
1805 m_body += generateVariantMapSetLookup(
1806 u"(*static_cast<const QVariantMap *>("_s
1807 + baseContentPointer + u"))"_s, index, property.storedType(),
1808 derefContentPointer(variableIn));
1809 generateWriteBack(baseReg);
1813 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1814 + u", "_s + baseContentPointer
1815 + u", "_s + variableIn + u')';
1819 const QString initialization
1820 = (property.contains(m_typeResolver->varType())
1821 ? u"aotContext->initSetValueLookupAsVariant("_s
1822 : u"aotContext->initSetValueLookup("_s)
1823 + indexString + u", "_s + metaObject(originalScope) + u')';
1825 generateLookup(lookup, initialization);
1826 generateWriteBack(baseReg);
1830 case QQmlJSScope::AccessSemantics::None:
1838void QQmlJSCodeGenerator::generate_LoadSuperProperty(
int property)
1844void QQmlJSCodeGenerator::generate_StoreSuperProperty(
int property)
1850void QQmlJSCodeGenerator::generate_Yield()
1855void QQmlJSCodeGenerator::generate_YieldStar()
1860void QQmlJSCodeGenerator::generate_Resume(
int)
1865QString QQmlJSCodeGenerator::initAndCall(
1866 int argc,
int argv,
const QString &callMethodTemplate,
const QString &initMethodTemplate,
1871 if (m_state.changedRegisterIndex() == InvalidRegister ||
1872 m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
1873 args = u"nullptr"_s;
1875 *outVar = u"callResult"_s;
1876 const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
1877 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1880 args = contentPointer(m_state.accumulatorOut(), *outVar);
1886 QString argumentPreparation;
1887 for (
int i = 0; i < argc; ++i) {
1888 const QQmlJSRegisterContent content = registerType(argv + i);
1889 const QQmlJSRegisterContent read = m_state.readRegister(argv + i);
1890 if (read.contains(content.containedType())) {
1891 args += u", "_s + contentPointer(read, registerVariable(argv + i));
1893 const QString var = u"arg"_s + QString::number(i);
1894 argumentPreparation +=
1895 u" "_s + read.storedType()->augmentedInternalName() + u' ' + var + u" = "_s
1896 + conversion(content, read, consumedRegisterVariable(argv + i)) + u";\n";
1897 args += u", "_s + contentPointer(read, var);
1903 if (m_state.isShadowable()) {
1904 initMethod = initMethodTemplate;
1906 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
1907 Q_ASSERT(!method.isConstructor());
1909 const QQmlJSMetaMethod::RelativeFunctionIndex relativeMethodIndex =
1910 method.isJavaScriptFunction() ? method.jsFunctionIndex() : method.methodIndex();
1911 initMethod = initMethodTemplate.arg(
int(relativeMethodIndex));
1914 return u"const auto doCall = [&]() {\n"_s
1915 + argumentPreparation
1916 + u" void *args[] = {" + args + u"};\n"_s
1917 + u" return aotContext->"_s + callMethodTemplate.arg(u"args"_s).arg(argc) + u";\n"
1919 + u"const auto doInit = [&]() {\n"_s
1920 + u" aotContext->"_s + initMethod + u";\n"
1924void QQmlJSCodeGenerator::generateMoveOutVarAfterCall(
const QString &outVar)
1926 if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
1929 m_body += m_state.accumulatorVariableOut + u" = "_s + u"std::move(" + outVar + u");\n";
1930 m_body += u"aotContext->setImplicitDestructible("_s
1931 + m_state.accumulatorVariableOut + u");\n"_s;
1934void QQmlJSCodeGenerator::generate_CallValue(
int name,
int argc,
int argv)
1942void QQmlJSCodeGenerator::generate_CallWithReceiver(
int name,
int thisObject,
int argc,
int argv)
1945 Q_UNUSED(thisObject)
1951void QQmlJSCodeGenerator::generate_CallProperty(
int nameIndex,
int baseReg,
int argc,
int argv)
1953 Q_UNUSED(nameIndex);
1957 REJECT(u"CallProperty"_s);
1960bool QQmlJSCodeGenerator::inlineStringMethod(
const QString &name,
int base,
int argc,
int argv)
1962 if (name != u"arg"_s || argc != 1)
1965 const auto arg = [&](
const QQmlJSScope::ConstPtr &type) {
1966 return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
1969 const auto ret = [&](
const QString &arg) {
1970 const QString expression = convertStored(
1971 registerType(base).storedType(), m_typeResolver->stringType(),
1972 consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
1974 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
1977 const QQmlJSRegisterContent input = m_state.readRegister(argv);
1978 m_body += m_state.accumulatorVariableOut + u" = "_s;
1980 if (m_typeResolver->isNumeric(input))
1981 m_body += ret(arg(input.containedType()));
1982 else if (input.contains(m_typeResolver->boolType()))
1983 m_body += ret(arg(m_typeResolver->boolType()));
1985 m_body += ret(arg(m_typeResolver->stringType()));
1990bool QQmlJSCodeGenerator::inlineTranslateMethod(
const QString &name,
int argc,
int argv)
1992 addInclude(u"QtCore/qcoreapplication.h"_s);
1994 const auto arg = [&](
int i,
const QQmlJSScope::ConstPtr &type) {
1996 return convertStored(registerType(argv + i).storedType(), type,
1997 consumedRegisterVariable(argv + i));
2000 const auto stringArg = [&](
int i) {
2002 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
2006 const auto intArg = [&](
int i) {
2007 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
2010 const auto stringRet = [&](
const QString &expression) {
2012 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
2015 const auto capture = [&]() {
2016 m_body += u"aotContext->captureTranslation();\n"_s;
2019 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
2021 m_body += m_state.accumulatorVariableOut + u" = "_s
2022 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
2026 if (name == u"QT_TRANSLATE_NOOP"_s) {
2028 m_body += m_state.accumulatorVariableOut + u" = "_s
2029 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
2033 if (name == u"qsTrId"_s) {
2038 m_body += m_state.accumulatorVariableOut + u" = "_s
2039 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
2040 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
2044 if (name == u"qsTr"_s) {
2046 m_body += m_state.accumulatorVariableOut + u" = "_s
2047 + stringRet(u"QCoreApplication::translate("_s
2048 + u"aotContext->translationContext().toUtf8().constData(), "_s
2049 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2050 + intArg(2) + u")"_s) + u";\n"_s;
2054 if (name == u"qsTranslate"_s) {
2056 m_body += m_state.accumulatorVariableOut + u" = "_s
2057 + stringRet(u"QCoreApplication::translate("_s
2058 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2059 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
2068 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"max() expects at least two arguments.");
2070 QString expression =
2071 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2072 for (
int i = 2; i < argc; i++) {
2074 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
2075 .arg(
"arg"_L1 + QString::number(i + 1));
2077 expression +=
"return tmpMax;\n}()"_L1;
2084 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"min() expects at least two arguments.");
2086 QString expression =
2087 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2088 for (
int i = 2; i < argc; i++) {
2090 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
2091 .arg(
"arg"_L1 + QString::number(i + 1));
2093 expression +=
"return tmpMin;\n}()"_L1;
2098bool QQmlJSCodeGenerator::inlineMathMethod(
const QString &name,
int argc,
int argv)
2100 addInclude(u"cmath"_s);
2101 addInclude(u"limits"_s);
2102 addInclude(u"QtCore/qalgorithms.h"_s);
2103 addInclude(u"QtCore/qrandom.h"_s);
2104 addInclude(u"QtQml/qjsprimitivevalue.h"_s);
2108 if (m_state.changedRegisterIndex() != Accumulator)
2112 for (
int i = 0; i < argc; ++i) {
2113 m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
2114 registerType(argv + i).storedType(),
2115 m_typeResolver->realType(), consumedRegisterVariable(argv + i))
2119 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
2120 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
2121 m_body += m_state.accumulatorVariableOut + u" = "_s;
2125 if (name == u"abs" && argc == 1) {
2126 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
2127 }
else if (name == u"acos"_s && argc == 1) {
2128 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
2129 }
else if (name == u"acosh"_s && argc == 1) {
2130 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
2131 }
else if (name == u"asin"_s && argc == 1) {
2132 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
2133 }
else if (name == u"asinh"_s && argc == 1) {
2134 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
2135 }
else if (name == u"atan"_s && argc == 1) {
2136 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
2137 }
else if (name == u"atanh"_s && argc == 1) {
2138 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
2139 }
else if (name == u"atan2"_s) {
2142 }
else if (name == u"cbrt"_s && argc == 1) {
2143 expression = u"std::cbrt(arg1)"_s;
2144 }
else if (name == u"ceil"_s && argc == 1) {
2145 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
2146 }
else if (name == u"clz32"_s && argc == 1) {
2147 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
2148 }
else if (name == u"cos"_s && argc == 1) {
2149 expression = u"std::cos(arg1)"_s;
2150 }
else if (name == u"cosh"_s && argc == 1) {
2151 expression = u"std::cosh(arg1)"_s;
2152 }
else if (name == u"exp"_s && argc == 1) {
2153 expression = u"std::isinf(arg1) "
2154 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
2155 ": std::exp(arg1)"_s.arg(inf);
2156 }
else if (name == u"expm1"_s) {
2159 }
else if (name == u"floor"_s && argc == 1) {
2160 expression = u"std::floor(arg1)"_s;
2161 }
else if (name == u"fround"_s && argc == 1) {
2162 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
2164 ": double(float(arg1))"_s;
2165 }
else if (name == u"hypot"_s) {
2168 }
else if (name == u"imul"_s && argc == 2) {
2169 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
2170 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
2171 }
else if (name == u"log"_s && argc == 1) {
2172 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
2173 }
else if (name == u"log10"_s && argc == 1) {
2174 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
2175 }
else if (name == u"log1p"_s && argc == 1) {
2176 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
2177 }
else if (name == u"log2"_s && argc == 1) {
2178 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
2179 }
else if (name == u"max"_s && argc >= 2) {
2180 expression = maxExpression(argc);
2181 }
else if (name == u"min"_s && argc >= 2) {
2182 expression = minExpression(argc);
2183 }
else if (name == u"pow"_s) {
2184 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
2185 }
else if (name == u"random"_s && argc == 0) {
2186 expression = u"QRandomGenerator::global()->generateDouble()"_s;
2187 }
else if (name == u"round"_s && argc == 1) {
2188 expression = u"std::isfinite(arg1) "
2189 "? ((arg1 < 0.5 && arg1 >= -0.5) "
2190 "? std::copysign(0.0, arg1) "
2191 ": std::floor(arg1 + 0.5)) "
2193 }
else if (name == u"sign"_s && argc == 1) {
2194 expression = u"std::isnan(arg1) "
2198 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
2199 }
else if (name == u"sin"_s && argc == 1) {
2200 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
2201 }
else if (name == u"sinh"_s && argc == 1) {
2202 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
2203 }
else if (name == u"sqrt"_s && argc == 1) {
2204 expression = u"std::sqrt(arg1)"_s;
2205 }
else if (name == u"tan"_s && argc == 1) {
2206 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
2207 }
else if (name == u"tanh"_s && argc == 1) {
2208 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
2209 }
else if (name == u"trunc"_s && argc == 1) {
2210 expression = u"std::trunc(arg1)"_s;
2215 m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
2224 if (method == u"log" || method == u"debug")
2225 return u"QtDebugMsg"_s;
2226 if (method == u"info")
2227 return u"QtInfoMsg"_s;
2228 if (method == u"warn")
2229 return u"QtWarningMsg"_s;
2230 if (method == u"error")
2231 return u"QtCriticalMsg"_s;
2235bool QQmlJSCodeGenerator::inlineConsoleMethod(
const QString &name,
int argc,
int argv)
2237 const QString type = messageTypeForMethod(name);
2241 addInclude(u"QtCore/qloggingcategory.h"_s);
2244 m_body += u" bool firstArgIsCategory = false;\n";
2245 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
2250 const bool firstArgIsReference = argc > 0
2251 && firstArg.containedType()->isReferenceType();
2253 if (firstArgIsReference) {
2254 m_body += u" QObject *firstArg = ";
2255 m_body += convertStored(
2256 firstArg.storedType(),
2257 m_typeResolver->genericType(firstArg.storedType()),
2258 registerVariable(argv));
2262 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
2263 m_body += firstArgIsReference ? u"firstArg"_sv : u"nullptr"_sv;
2264 m_body += u", &firstArgIsCategory);\n";
2265 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
2267 m_body += u" const QString message = ";
2269 const auto stringConversion = [&](
int i) -> QString {
2270 const QQmlJSScope::ConstPtr read = m_state.readRegister(argv + i).storedType();
2271 const QQmlJSScope::ConstPtr actual = registerType(argv + i).storedType();
2272 if (read == m_typeResolver->stringType()) {
2273 return convertStored(actual, read, consumedRegisterVariable(argv + i));
2274 }
else if (actual->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2275 addInclude(u"QtQml/qjslist.h"_s);
2276 return u"(u'[' + QJSList(&"_s + registerVariable(argv + i)
2277 + u", aotContext->engine).toString() + u']')"_s;
2279 REJECT<QString>(u"converting arguments for console method to string"_s);
2284 if (firstArgIsReference) {
2285 const QString firstArgStringConversion = convertStored(
2286 registerType(argv).storedType(),
2287 m_typeResolver->stringType(), registerVariable(argv));
2288 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
2290 m_body += u".append(QLatin1Char(' ')))).append(";
2294 m_body += stringConversion(0);
2296 m_body += u".append(QLatin1Char(' ')).append(";
2299 for (
int i = 1; i < argc; ++i) {
2301 m_body += u".append(QLatin1Char(' ')).append("_s;
2302 m_body += stringConversion(i) + u')';
2305 m_body += u"QString()";
2308 generateSetInstructionPointer();
2309 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
2315bool QQmlJSCodeGenerator::inlineArrayMethod(
const QString &name,
int base,
int argc,
int argv)
2317 const auto intType = m_typeResolver->int32Type();
2318 const auto elementType = registerType(base).storedType()->elementType();
2319 const auto boolType = m_typeResolver->boolType();
2320 const auto stringType = m_typeResolver->stringType();
2321 const auto baseType = registerType(base);
2323 const QString baseVar = registerVariable(base);
2324 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
2327 addInclude(u"QtQml/qjslist.h"_s);
2329 if (name == u"includes" && argc > 0 && argc < 3) {
2330 QString call = qjsListMethod
2331 + convertStored(registerType(argv).storedType(), elementType,
2332 consumedRegisterVariable(argv));
2334 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2335 consumedRegisterVariable(argv + 1));
2339 m_body += m_state.accumulatorVariableOut + u" = "_s
2340 + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
2344 if (name == u"toString" || (name == u"join" && argc < 2)) {
2345 QString call = qjsListMethod;
2347 call += convertStored(registerType(argv).storedType(), stringType,
2348 consumedRegisterVariable(argv));
2352 m_body += m_state.accumulatorVariableOut + u" = "_s
2353 + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
2357 if (name == u"slice" && argc < 3) {
2358 QString call = qjsListMethod;
2359 for (
int i = 0; i < argc; ++i) {
2362 call += convertStored(registerType(argv + i).storedType(), intType,
2363 consumedRegisterVariable(argv + i));
2367 m_body += m_state.accumulatorVariableOut + u" = "_s;
2368 if (baseType.storedType()->isListProperty())
2369 m_body += conversion(m_typeResolver->qObjectListType(), m_state.accumulatorOut(), call);
2371 m_body += conversion(baseType, m_state.accumulatorOut(), call);
2377 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2378 QString call = qjsListMethod
2379 + convertStored(registerType(argv).storedType(), elementType,
2380 consumedRegisterVariable(argv));
2382 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2383 consumedRegisterVariable(argv + 1));
2387 m_body += m_state.accumulatorVariableOut + u" = "_s
2388 + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
2395void QQmlJSCodeGenerator::generate_CallPropertyLookup(
int index,
int base,
int argc,
int argv)
2399 const QQmlJSRegisterContent scopeContent = m_state.accumulatorOut().scope();
2400 const QQmlJSScope::ConstPtr scope = scopeContent.containedType();
2402 AccumulatorConverter registers(
this);
2404 const QQmlJSRegisterContent baseType = registerType(base);
2405 const QString name = m_jsUnitGenerator->lookupName(index);
2407 if (scope == m_typeResolver->mathObject()) {
2408 if (inlineMathMethod(name, argc, argv))
2410 }
else if (scope == m_typeResolver->consoleObject()) {
2411 if (inlineConsoleMethod(name, argc, argv))
2413 }
else if (scope == m_typeResolver->stringType()) {
2414 if (inlineStringMethod(name, base, argc, argv))
2416 }
else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2417 if (inlineArrayMethod(name, base, argc, argv))
2421 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2422 REJECT(u"call to untyped JavaScript function"_s);
2427 if (scope->isReferenceType()) {
2428 const QString inputPointer = resolveQObjectPointer(
2429 scope, baseType, registerVariable(base),
2430 u"Cannot call method '%1' of %2"_s.arg(name));
2432 const QString initMethodTemplate = m_state.isShadowable()
2433 ? u"initCallObjectPropertyLookupAsVariant(%1, %2)"_s
2434 : u"initCallObjectPropertyLookup(%1, %2, %3)"_s;
2436 m_body += initAndCall(
2438 u"callObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2439 initMethodTemplate.arg(index).arg(inputPointer), &outVar);
2441 const QQmlJSScope::ConstPtr originalScope
2442 = m_typeResolver->original(scopeContent).containedType();
2443 const QString inputPointer = resolveValueTypeContentPointer(
2444 originalScope, baseType, registerVariable(base),
2445 u"Cannot call method '%1' of %2"_s.arg(name));
2447 m_body += initAndCall(
2449 u"callValueLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2450 u"initCallValueLookup(%1, %2, %3)"_s
2451 .arg(index).arg(metaObject(originalScope)),
2455 const QString lookup = u"doCall()"_s;
2456 const QString initialization = u"doInit()"_s;
2457 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2458 generateLookup(lookup, initialization, preparation);
2459 generateMoveOutVarAfterCall(outVar);
2463 if (scope->isReferenceType())
2466 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
2467 if (!method.isConst())
2468 generateWriteBack(base);
2471void QQmlJSCodeGenerator::generate_CallName(
int name,
int argc,
int argv)
2479void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(
int argc,
int argv)
2486void QQmlJSCodeGenerator::generate_CallGlobalLookup(
int index,
int argc,
int argv)
2491 REJECT(u"CallGlobalLookup"_s);
2494void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(
int index,
int argc,
int argv)
2498 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
2499 const QString name = m_jsUnitGenerator->stringForIndex(
2500 m_jsUnitGenerator->lookupNameIndex(index));
2501 if (inlineTranslateMethod(name, argc, argv))
2505 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2506 REJECT(u"call to untyped JavaScript function"_s);
2508 AccumulatorConverter registers(
this);
2512 m_body += initAndCall(
2513 argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
2514 u"initCallQmlContextPropertyLookup(%1, %2)"_s.arg(index), &outVar);
2516 const QString lookup = u"doCall()"_s;
2517 const QString initialization = u"doInit()"_s;
2518 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2519 generateLookup(lookup, initialization, preparation);
2520 generateMoveOutVarAfterCall(outVar);
2525void QQmlJSCodeGenerator::generate_CallWithSpread(
int func,
int thisObject,
int argc,
int argv)
2528 Q_UNUSED(thisObject)
2534void QQmlJSCodeGenerator::generate_TailCall(
int func,
int thisObject,
int argc,
int argv)
2537 Q_UNUSED(thisObject)
2543void QQmlJSCodeGenerator::generate_Construct(
int func,
int argc,
int argv)
2548 const auto originalResult = originalType(m_state.accumulatorOut());
2550 if (originalResult.contains(m_typeResolver->dateTimeType())) {
2551 m_body += m_state.accumulatorVariableOut + u" = ";
2553 m_body += conversion(
2554 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2555 u"QDateTime::currentDateTime()"_s) + u";\n";
2559 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->dateTimeType())) {
2560 m_body += conversion(
2561 registerType(argv), m_state.readRegister(argv), registerVariable(argv))
2567 constexpr int maxArgc = 7;
2568 for (
int i = 0; i < std::min(argc, maxArgc); ++i) {
2571 ctorArgs += conversion(
2572 registerType(argv + i), m_state.readRegister(argv + i),
2573 registerVariable(argv + i));
2575 m_body += conversion(
2576 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2577 u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
2581 if (originalResult.contains(m_typeResolver->variantListType())) {
2584 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->realType())) {
2585 addInclude(u"QtQml/qjslist.h"_s);
2587 const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
2588 + u"QLatin1String(\"Invalid array length\"));\n"_s;
2590 const QString indexName = registerVariable(argv);
2591 const auto indexType = registerType(argv).containedType();
2592 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
2593 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
2595 generateReturnError();
2597 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
2598 m_body += u"if ("_s + indexName + u" < 0) {\n"_s
2600 generateReturnError();
2604 m_body += m_state.accumulatorVariableOut + u" = "_s
2605 + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
2606 m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
2607 + u", aotContext->engine).resize("_s
2609 registerType(argv).storedType(), m_typeResolver->sizeType(),
2610 consumedRegisterVariable(argv))
2612 }
else if (!m_logger->currentFunctionHasCompileError()) {
2613 generateArrayInitializer(argc, argv);
2618 const QQmlJSScope::ConstPtr originalContained = originalResult.containedType();
2619 if (originalContained->isValueType() && originalResult.isMethodCall()) {
2620 const QQmlJSMetaMethod ctor = originalResult.methodCall();
2621 if (ctor.isJavaScriptFunction())
2622 REJECT(u"calling JavaScript constructor "_s + ctor.methodName());
2624 QList<QQmlJSRegisterContent> argumentTypes;
2625 QStringList arguments;
2626 for (
int i = 0; i < argc; ++i) {
2627 argumentTypes.append(registerType(argv + i));
2628 arguments.append(consumedRegisterVariable(argv + i));
2631 const QQmlJSScope::ConstPtr extension = originalContained->extensionType().scope;
2632 const QString result = generateCallConstructor(
2633 ctor, argumentTypes, arguments, metaType(originalContained),
2634 metaObject(extension ? extension : originalContained));
2636 m_body += m_state.accumulatorVariableOut + u" = "_s
2637 + conversion(m_pool->storedIn(originalResult, m_typeResolver->varType()),
2638 m_state.accumulatorOut(), result)
2648void QQmlJSCodeGenerator::generate_ConstructWithSpread(
int func,
int argc,
int argv)
2656void QQmlJSCodeGenerator::generate_SetUnwindHandler(
int offset)
2659 REJECT(u"SetUnwindHandler"_s);
2662void QQmlJSCodeGenerator::generate_UnwindDispatch()
2664 REJECT(u"UnwindDispatch"_s);
2667void QQmlJSCodeGenerator::generate_UnwindToLabel(
int level,
int offset)
2674void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(
int name)
2682void QQmlJSCodeGenerator::generate_ThrowException()
2686 generateSetInstructionPointer();
2687 m_body += u"aotContext->engine->throwError("_s + conversion(
2688 m_state.accumulatorIn(),
2689 m_typeResolver->jsValueType(),
2690 m_state.accumulatorVariableIn) + u");\n"_s;
2691 generateReturnError();
2692 m_skipUntilNextLabel =
true;
2696void QQmlJSCodeGenerator::generate_GetException()
2701void QQmlJSCodeGenerator::generate_SetException()
2706void QQmlJSCodeGenerator::generate_CreateCallContext()
2713void QQmlJSCodeGenerator::generate_PushCatchContext(
int index,
int nameIndex)
2717 REJECT(u"PushCatchContext"_s);
2720void QQmlJSCodeGenerator::generate_PushWithContext()
2725void QQmlJSCodeGenerator::generate_PushBlockContext(
int index)
2731void QQmlJSCodeGenerator::generate_CloneBlockContext()
2736void QQmlJSCodeGenerator::generate_PushScriptContext(
int index)
2742void QQmlJSCodeGenerator::generate_PopScriptContext()
2747void QQmlJSCodeGenerator::generate_PopContext()
2752 m_body += u"{}\n}\n"_s;
2755void QQmlJSCodeGenerator::generate_GetIterator(
int iterator)
2759 addInclude(u"QtQml/qjslist.h"_s);
2760 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2761 if (!listType.isList())
2762 REJECT(u"iterator on non-list type"_s);
2764 const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
2765 if (!iteratorType.isProperty())
2766 REJECT(u"using non-iterator as iterator"_s);
2768 const QString identifier = QString::number(iteratorType.baseLookupIndex());
2769 QString baseName = m_state.accumulatorVariableOut.mid(2);
2770 const QString iteratorName = baseName + u"Iterator" + identifier;
2771 const QString listName = baseName + u"List" + identifier;
2773 m_body += u"QJSListFor"_s
2774 + (iterator ==
int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
2775 + u"Iterator "_s + iteratorName + u";\n";
2776 m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
2778 m_body += m_state.accumulatorVariableOut + u"->init(";
2779 if (iterator ==
int(QQmlJS::AST::ForEachType::In)) {
2780 if (!iteratorType.isStoredIn(m_typeResolver->forInIteratorPtr()))
2781 REJECT(u"using non-iterator as iterator"_s);
2782 m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
2786 if (iterator ==
int(QQmlJS::AST::ForEachType::Of)) {
2787 if (!iteratorType.isStoredIn(m_typeResolver->forOfIteratorPtr()))
2788 REJECT(u"using non-iterator as iterator"_s);
2789 m_body += u"const auto &"
2790 + listName + u" = " + consumedAccumulatorVariableIn();
2794void QQmlJSCodeGenerator::generate_IteratorNext(
int value,
int offset)
2798 Q_ASSERT(value == m_state.changedRegisterIndex());
2799 const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
2800 if (!iteratorContent.isProperty())
2801 REJECT(u"using non-iterator as iterator"_s);
2803 const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
2804 const QString listName = m_state.accumulatorVariableIn
2805 + u"List" + QString::number(iteratorContent.baseLookupIndex());
2807 if (iteratorType == m_typeResolver->forOfIteratorPtr())
2808 qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
2809 else if (iteratorType != m_typeResolver->forInIteratorPtr())
2810 REJECT(u"using non-iterator as iterator"_s);
2812 m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
2815 QQmlJSRegisterContent iteratorValue = m_typeResolver->extractNonVoidFromOptionalType(
2816 m_typeResolver->original(m_state.changedRegister()));
2817 iteratorValue = m_pool->storedIn(iteratorValue, iteratorValue.containedType());
2819 m_body += changedRegisterVariable() + u" = "
2821 iteratorValue, m_state.changedRegister(),
2822 m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
2824 m_body += u"} else {\n ";
2825 m_body += changedRegisterVariable() + u" = "
2826 + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
2828 generateJumpCodeWithTypeConversions(offset);
2832void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(
int iterator,
int object,
int offset)
2840void QQmlJSCodeGenerator::generate_IteratorClose()
2845void QQmlJSCodeGenerator::generate_DestructureRestElement()
2850void QQmlJSCodeGenerator::generate_DeleteProperty(
int base,
int index)
2857void QQmlJSCodeGenerator::generate_DeleteName(
int name)
2863void QQmlJSCodeGenerator::generate_TypeofName(
int name)
2869void QQmlJSCodeGenerator::generate_TypeofValue()
2871 REJECT(u"TypeofValue"_s);
2874void QQmlJSCodeGenerator::generate_DeclareVar(
int varName,
int isDeletable)
2877 Q_UNUSED(isDeletable)
2881void QQmlJSCodeGenerator::generate_DefineArray(
int argc,
int args)
2886 if (!m_logger->currentFunctionHasCompileError())
2887 generateArrayInitializer(argc, args);
2890void QQmlJSCodeGenerator::generate_DefineObjectLiteral(
int internalClassId,
int argc,
int args)
2894 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
2895 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value)
2896 REJECT(u"storing an object literal in a non-value type"_s);
2898 const QQmlJSScope::ConstPtr contained = m_state.accumulatorOut().containedType();
2900 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2901 Q_ASSERT(argc >= classSize);
2903 const auto createVariantMap = [&]() {
2905 result += u"QVariantMap {\n";
2906 const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
2907 for (
int i = 0; i < classSize; ++i) {
2909 + QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
2911 const int currentArg = args + i;
2912 const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
2913 const QString consumedArg = consumedRegisterVariable(currentArg);
2914 result += convertStored(argType, propType, consumedArg) + u" },\n";
2917 for (
int i = classSize; i < argc; i += 3) {
2918 const int nameArg = args + i + 1;
2921 registerType(nameArg),
2922 m_typeResolver->stringType(),
2923 consumedRegisterVariable(nameArg))
2926 const int valueArg = args + i + 2;
2927 result += convertStored(
2928 registerType(valueArg).storedType(),
2930 consumedRegisterVariable(valueArg))
2938 if (contained == m_typeResolver->varType() || contained == m_typeResolver->variantMapType()) {
2939 m_body += m_state.accumulatorVariableOut + u" = "_s + createVariantMap() + u";\n"_s;
2943 if (contained == m_typeResolver->jsValueType()) {
2944 m_body += m_state.accumulatorVariableOut + u" = aotContext->engine->toScriptValue("_s
2945 + createVariantMap() + u");\n"_s;
2949 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
2950 const bool isVariantOrPrimitive = (stored == m_typeResolver->varType())
2951 || (stored == m_typeResolver->jsPrimitiveType());
2953 if (m_state.accumulatorOut().contains(stored)) {
2955 }
else if (isVariantOrPrimitive) {
2956 m_body += u'(' + metaType(m_state.accumulatorOut().containedType()) + u')';
2958 REJECT(u"storing an object literal in an unsupported container %1"_s
2959 .arg(stored->internalName()));
2966 bool isExtension =
false;
2967 if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension))
2968 REJECT(u"storing an object literal in a non-structured value type"_s);
2970 const QQmlJSScope::ConstPtr accessor = isExtension
2971 ? contained->extensionType().scope
2975 m_body += u" const QMetaObject *meta = ";
2976 if (!isExtension && isVariantOrPrimitive)
2977 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
2979 m_body += metaObject(accessor);
2982 for (
int i = 0; i < classSize; ++i) {
2984 const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
2985 const int currentArg = args + i;
2986 const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
2987 const QQmlJSRegisterContent argType = registerType(currentArg);
2988 const QQmlJSMetaProperty property = contained->property(propName);
2989 const QString consumedArg = consumedRegisterVariable(currentArg);
2990 QString argument = conversion(argType, propType, consumedArg);
2992 if (argument == consumedArg) {
2993 argument = registerVariable(currentArg);
2995 m_body += u" "_s + propType.storedType()->augmentedInternalName()
2996 + u" arg = "_s + argument + u";\n";
2997 argument = u"arg"_s;
3000 int index = property.index();
3004 const QString indexString = QString::number(index);
3005 m_body += u" void *argv[] = { %1, nullptr };\n"_s
3006 .arg(contentPointer(propType, argument));
3007 m_body += u" meta->property("_s + indexString;
3008 m_body += u").enclosingMetaObject()->d.static_metacall(reinterpret_cast<QObject *>(";
3009 m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
3010 m_body += u"), QMetaObject::WriteProperty, " + indexString + u", argv);\n";
3019 if (argc > classSize)
3020 REJECT(u"non-literal keys of object literals"_s);
3026void QQmlJSCodeGenerator::generate_CreateClass(
int classIndex,
int heritage,
int computedNames)
3028 Q_UNUSED(classIndex)
3030 Q_UNUSED(computedNames)
3034void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
3039void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
3044void QQmlJSCodeGenerator::generate_CreateRestParameter(
int argIndex)
3050void QQmlJSCodeGenerator::generate_ConvertThisToObject()
3054 m_body += changedRegisterVariable() + u" = "_s
3055 + conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
3056 u"aotContext->thisObject()"_s)
3060void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
3065void QQmlJSCodeGenerator::generate_ToObject()
3070void QQmlJSCodeGenerator::generate_Jump(
int offset)
3074 generateJumpCodeWithTypeConversions(offset);
3075 m_skipUntilNextLabel =
true;
3079void QQmlJSCodeGenerator::generate_JumpTrue(
int offset)
3083 m_body += u"if ("_s;
3084 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3085 m_state.accumulatorVariableIn);
3087 generateJumpCodeWithTypeConversions(offset);
3090void QQmlJSCodeGenerator::generate_JumpFalse(
int offset)
3094 m_body += u"if (!"_s;
3095 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3096 m_state.accumulatorVariableIn);
3098 generateJumpCodeWithTypeConversions(offset);
3101void QQmlJSCodeGenerator::generate_JumpNoException(
int offset)
3105 m_body += u"if (!context->engine->hasException()) "_s;
3106 generateJumpCodeWithTypeConversions(offset);
3109void QQmlJSCodeGenerator::generate_JumpNotUndefined(
int offset)
3115void QQmlJSCodeGenerator::generate_CheckException()
3119 generateExceptionCheck();
3122void QQmlJSCodeGenerator::generate_CmpEqNull()
3125 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
false);
3128void QQmlJSCodeGenerator::generate_CmpNeNull()
3131 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
true);
3134QString QQmlJSCodeGenerator::getLookupPreparation(
3135 QQmlJSRegisterContent content,
const QString &var,
int lookup)
3137 if (content.contains(content.storedType()))
3140 if (content.isStoredIn(m_typeResolver->varType())) {
3141 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
3142 + QString::number(lookup) + u"))"_s;
3145 if (content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3146 return var + u" = QJSPrimitiveValue(aotContext->lookupResultMetaType("_s
3147 + QString::number(lookup) + u"))"_s;
3154QString QQmlJSCodeGenerator::contentPointer(QQmlJSRegisterContent content,
const QString &var)
3156 const QQmlJSScope::ConstPtr stored = content.storedType();
3157 if (content.contains(stored))
3160 if (content.isStoredIn(m_typeResolver->varType())
3161 || content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3162 return var + u".data()"_s;
3165 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3168 if (m_typeResolver->isNumeric(content.storedType())
3169 && content.containedType()->scopeType() == QQmlSA::ScopeType::EnumScope) {
3173 if (stored->isListProperty() && content.containedType()->isListProperty())
3177 u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
3180QString QQmlJSCodeGenerator::contentType(QQmlJSRegisterContent content,
const QString &var)
3182 const QQmlJSScope::ConstPtr stored = content.storedType();
3183 const QQmlJSScope::ConstPtr contained = content.containedType();
3184 if (contained == stored)
3185 return metaTypeFromType(stored);
3187 if (stored == m_typeResolver->varType() || stored == m_typeResolver->jsPrimitiveType())
3188 return var + u".metaType()"_s;
3190 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3191 return metaType(contained);
3193 const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
3194 if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
3195 return metaTypeFromType(nonComposite->baseType());
3197 if (stored->isListProperty() && contained->isListProperty())
3198 return metaType(contained);
3201 u"content type of unsupported wrapper type "_s + content.descriptiveName());
3204void QQmlJSCodeGenerator::generate_CmpEqInt(
int lhsConst)
3208 generateEqualityOperation(
3209 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
false);
3212void QQmlJSCodeGenerator::generate_CmpNeInt(
int lhsConst)
3216 generateEqualityOperation(
3217 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
true);
3220void QQmlJSCodeGenerator::generate_CmpEq(
int lhs)
3223 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
false);
3226void QQmlJSCodeGenerator::generate_CmpNe(
int lhs)
3229 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
true);
3232void QQmlJSCodeGenerator::generate_CmpGt(
int lhs)
3235 generateCompareOperation(lhs, u">"_s);
3238void QQmlJSCodeGenerator::generate_CmpGe(
int lhs)
3241 generateCompareOperation(lhs, u">="_s);
3244void QQmlJSCodeGenerator::generate_CmpLt(
int lhs)
3247 generateCompareOperation(lhs, u"<"_s);
3250void QQmlJSCodeGenerator::generate_CmpLe(
int lhs)
3253 generateCompareOperation(lhs, u"<="_s);
3256void QQmlJSCodeGenerator::generate_CmpStrictEqual(
int lhs)
3259 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
false);
3262void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(
int lhs)
3265 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
true);
3268void QQmlJSCodeGenerator::generate_CmpIn(
int lhs)
3274void QQmlJSCodeGenerator::generate_CmpInstanceOf(
int lhs)
3280void QQmlJSCodeGenerator::generate_As(
int lhs)
3284 const QString input = registerVariable(lhs);
3285 const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
3286 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
3290 const QQmlJSRegisterContent originalContent = originalType(outputContent);
3291 QQmlJSScope::ConstPtr target;
3292 if (originalContent.containedType()->isReferenceType())
3293 target = originalContent.containedType();
3294 else if (originalContent.isConversion())
3295 target = m_typeResolver->extractNonVoidFromOptionalType(originalContent).containedType();
3296 else if (originalContent.variant() == QQmlJSRegisterContent::Cast)
3297 target = originalContent.containedType();
3300 REJECT(u"type assertion to unknown type"_s);
3302 const bool isTrivial = m_typeResolver->inherits(
3303 m_typeResolver->originalContainedType(inputContent), target);
3305 m_body += m_state.accumulatorVariableOut + u" = "_s;
3307 if (!isTrivial && target->isReferenceType()) {
3308 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
3309 const QString inputConversion = inputContent.storedType()->isReferenceType()
3311 : convertStored(inputContent.storedType(), genericContained, input);
3313 if (target->isComposite()
3314 && m_state.accumulatorIn().isStoredIn(m_typeResolver->metaObjectType())) {
3315 m_body += conversion(
3316 genericContained, outputContent,
3317 m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
3319 m_body += conversion(
3320 genericContained, outputContent,
3321 u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
3327 if (inputContent.isStoredIn(m_typeResolver->varType())
3328 || inputContent.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3330 const auto source = m_typeResolver->extractNonVoidFromOptionalType(
3331 originalType(inputContent)).containedType();
3333 if (source && source == target) {
3334 m_body += input + u".metaType() == "_s + metaType(target)
3335 + u" ? " + conversion(inputContent, outputContent, input)
3336 + u" : " + conversion(
3337 literalType(m_typeResolver->voidType()), outputContent, QString());
3345 m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
3349 REJECT(u"non-trivial value type assertion"_s);
3352void QQmlJSCodeGenerator::generate_UNot()
3355 generateUnaryOperation(u"!"_s);
3358void QQmlJSCodeGenerator::generate_UPlus()
3361 generateUnaryOperation(u"+"_s);
3364void QQmlJSCodeGenerator::generate_UMinus()
3367 generateUnaryOperation(u"-"_s);
3370void QQmlJSCodeGenerator::generate_UCompl()
3373 generateUnaryOperation(u"~"_s);
3376void QQmlJSCodeGenerator::generate_Increment()
3379 generateInPlaceOperation(u"++"_s);
3382void QQmlJSCodeGenerator::generate_Decrement()
3385 generateInPlaceOperation(u"--"_s);
3388void QQmlJSCodeGenerator::generate_Add(
int lhs)
3391 generateArithmeticOperation(lhs, u"+"_s);
3394void QQmlJSCodeGenerator::generate_BitAnd(
int lhs)
3397 generateArithmeticOperation(lhs, u"&"_s);
3400void QQmlJSCodeGenerator::generate_BitOr(
int lhs)
3403 generateArithmeticOperation(lhs, u"|"_s);
3406void QQmlJSCodeGenerator::generate_BitXor(
int lhs)
3409 generateArithmeticOperation(lhs, u"^"_s);
3412void QQmlJSCodeGenerator::generate_UShr(
int lhs)
3415 generateShiftOperation(lhs, u">>"_s);
3418void QQmlJSCodeGenerator::generate_Shr(
int lhs)
3421 generateShiftOperation(lhs, u">>"_s);
3424void QQmlJSCodeGenerator::generate_Shl(
int lhs)
3427 generateShiftOperation(lhs, u"<<"_s);
3430void QQmlJSCodeGenerator::generate_BitAndConst(
int rhs)
3433 generateArithmeticConstOperation(rhs, u"&"_s);
3436void QQmlJSCodeGenerator::generate_BitOrConst(
int rhs)
3439 generateArithmeticConstOperation(rhs, u"|"_s);
3442void QQmlJSCodeGenerator::generate_BitXorConst(
int rhs)
3445 generateArithmeticConstOperation(rhs, u"^"_s);
3448void QQmlJSCodeGenerator::generate_UShrConst(
int rhs)
3451 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3454void QQmlJSCodeGenerator::generate_ShrConst(
int rhs)
3457 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3460void QQmlJSCodeGenerator::generate_ShlConst(
int rhs)
3463 generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
3466void QQmlJSCodeGenerator::generate_Exp(
int lhs)
3470 const QString lhsString = conversion(
3471 registerType(lhs), m_state.readRegister(lhs), consumedRegisterVariable(lhs));
3472 const QString rhsString = conversion(
3473 m_state.accumulatorIn(), m_state.readAccumulator(),
3474 consumedAccumulatorVariableIn());
3476 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsString.isEmpty());
3477 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsString.isEmpty());
3479 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3480 m_body += m_state.accumulatorVariableOut + u" = "_s;
3481 m_body += conversion(
3482 originalOut, m_state.accumulatorOut(),
3483 u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
3487void QQmlJSCodeGenerator::generate_Mul(
int lhs)
3490 generateArithmeticOperation(lhs, u"*"_s);
3493void QQmlJSCodeGenerator::generate_Div(
int lhs)
3496 generateArithmeticOperation(lhs, u"/"_s);
3499void QQmlJSCodeGenerator::generate_Mod(
int lhs)
3503 const auto lhsVar = convertStored(
3504 registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
3505 consumedRegisterVariable(lhs));
3506 const auto rhsVar = convertStored(
3507 m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
3508 consumedAccumulatorVariableIn());
3509 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsVar.isEmpty());
3510 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsVar.isEmpty());
3512 m_body += m_state.accumulatorVariableOut;
3514 m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
3515 u'(' + lhsVar + u" % "_s + rhsVar + u')');
3519void QQmlJSCodeGenerator::generate_Sub(
int lhs)
3522 generateArithmeticOperation(lhs, u"-"_s);
3525void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(
int firstReg,
int count)
3532void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
3537void QQmlJSCodeGenerator::generate_GetTemplateObject(
int index)
3543QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
3544 QV4::Moth::Instr::Type type)
3546 m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
3547 const auto accumulatorIn = m_state.registers.find(Accumulator);
3548 if (accumulatorIn != m_state.registers.end()
3549 && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
3550 QQmlJSRegisterContent content = accumulatorIn.value().content;
3551 m_state.accumulatorVariableIn = m_registerVariables.value(content).variableName;
3552 Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
3554 m_state.accumulatorVariableIn.clear();
3557 auto labelIt = m_labels.constFind(currentInstructionOffset());
3558 if (labelIt != m_labels.constEnd()) {
3559 m_body += *labelIt + u":;\n"_s;
3560 m_skipUntilNextLabel =
false;
3561 }
else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
3562 return SkipInstruction;
3565 if (m_state.changedRegisterIndex() == Accumulator)
3566 m_state.accumulatorVariableOut = changedRegisterVariable();
3568 m_state.accumulatorVariableOut.clear();
3572 Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
3573 || m_state.accumulatorVariableOut.isEmpty());
3574 Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
3575 || !m_state.accumulatorVariableOut.isEmpty()
3576 || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
3580 if (!m_state.hasInternalSideEffects() && changedRegisterVariable().isEmpty()) {
3581 generateJumpCodeWithTypeConversions(0);
3582 return SkipInstruction;
3585 return ProcessInstruction;
3588void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
3590 if (!m_skipUntilNextLabel)
3591 generateJumpCodeWithTypeConversions(0);
3592 m_pool->clearTemporaries();
3595void QQmlJSCodeGenerator::generateSetInstructionPointer()
3597 m_body += u"aotContext->setInstructionPointer("_s
3598 + QString::number(nextInstructionOffset()) + u");\n"_s;
3601void QQmlJSCodeGenerator::generateExceptionCheck()
3603 m_body += u"if (aotContext->engine->hasError()) {\n"_s;
3604 generateReturnError();
3608void QQmlJSCodeGenerator::generateEqualityOperation(
3609 QQmlJSRegisterContent lhsContent, QQmlJSRegisterContent rhsContent,
3610 const QString &lhsName,
const QString &rhsName,
const QString &function,
bool invert)
3612 const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
3613 const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
3615 const QQmlJSScope::ConstPtr rhsContained = rhsIsOptional
3616 ? m_typeResolver->extractNonVoidFromOptionalType(rhsContent).containedType()
3617 : rhsContent.containedType();
3619 const QQmlJSScope::ConstPtr lhsContained = lhsIsOptional
3620 ? m_typeResolver->extractNonVoidFromOptionalType(lhsContent).containedType()
3621 : lhsContent.containedType();
3623 const bool isStrict = function ==
"strictlyEquals"_L1;
3624 const bool strictlyComparableWithVar
3625 = isStrict && canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
3626 auto isComparable = [&]() {
3627 if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
3629 if (m_typeResolver->isNumeric(lhsContent) && m_typeResolver->isNumeric(rhsContent))
3631 if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
3633 if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
3635 if (strictlyComparableWithVar)
3637 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
3639 if (canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
3644 const auto retrieveOriginal = [
this](QQmlJSRegisterContent content) {
3645 const auto contained = content.containedType();
3646 const auto originalContent = originalType(content);
3647 const auto containedOriginal = originalContent.containedType();
3649 if (originalContent.isStoredIn(m_typeResolver->genericType(containedOriginal))) {
3651 return originalContent;
3652 }
else if (contained == containedOriginal) {
3653 if (originalContent.isConversion()) {
3655 return m_pool->storedIn(originalContent, content.storedType());
3657 }
else if (m_typeResolver->canHold(contained, containedOriginal)) {
3658 return m_pool->storedIn(originalContent, content.storedType());
3664 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
3665 const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
3667 if (!isComparable()) {
3668 QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
3669 QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
3670 if (lhsOriginal.containedType() != lhsContent.containedType()
3671 || lhsOriginal.storedType() != lhsType
3672 || rhsOriginal.containedType() != rhsContent.containedType()
3673 || rhsOriginal.storedType() != rhsType) {
3676 generateEqualityOperation(lhsOriginal, rhsOriginal,
3677 conversion(lhsType, lhsOriginal, lhsName),
3678 conversion(rhsType, rhsOriginal, rhsName),
3683 REJECT(u"incomparable types %1 and %2"_s.arg(
3684 rhsContent.descriptiveName(), lhsContent.descriptiveName()));
3687 if (strictlyComparableWithVar) {
3689 if (!lhsName.isEmpty() && rhsName.isEmpty()) {
3691 generateVariantEqualityComparison(rhsContent, lhsName, invert);
3695 if (!rhsName.isEmpty() && lhsName.isEmpty()) {
3697 generateVariantEqualityComparison(lhsContent, rhsName, invert);
3701 if (lhsContent.contains(m_typeResolver->varType())) {
3702 generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
3706 if (rhsContent.contains(m_typeResolver->varType())) {
3707 generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
3713 REJECT(u"comparison of optional null"_s);
3716 const auto comparison = [&]() -> QString {
3717 const auto primitive = m_typeResolver->jsPrimitiveType();
3718 const QString sign = invert ? u" != "_s : u" == "_s;
3720 if (lhsType == rhsType && lhsType != primitive && lhsType != m_typeResolver->varType()) {
3725 if (isTypeStorable(m_typeResolver, lhsType))
3726 return lhsName + sign + rhsName;
3729 return invert ? u"false"_s : u"true"_s;
3732 if (canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
3734 return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
3736 + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
3739 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
3746 if (lhsIsOptional) {
3747 if (rhsIsOptional) {
3750 + lhsName + u".isValid() && !"_s
3751 + rhsName + u".isValid()) || "_s;
3754 result += u'(' + lhsName + u".isValid() && "_s;
3759 if (rhsIsOptional) {
3760 result += rhsName + u".isValid() && "_s;
3767 Q_ASSERT(lhsType != m_typeResolver->voidType());
3768 Q_ASSERT(rhsType != m_typeResolver->voidType());
3770 const auto resolvedName = [&](
const QString name) -> QString {
3772 const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
3775 : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
3778 const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
3779 const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
3781 return (invert ? u"!("_s : u"("_s) + result
3782 + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
3784 + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
3788 if ((m_typeResolver->isUnsignedInteger(rhsType)
3789 && m_typeResolver->isUnsignedInteger(lhsType))
3790 || (m_typeResolver->isSignedInteger(rhsType)
3791 && m_typeResolver->isSignedInteger(lhsType))) {
3793 return lhsName + sign + rhsName;
3796 if (rhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(lhsType)) {
3798 return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
3801 if (lhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(rhsType)) {
3803 return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
3806 if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
3808 return convertStored(lhsType, m_typeResolver->realType(), lhsName)
3810 + convertStored(rhsType, m_typeResolver->realType(), rhsName);
3814 return (invert ? u"!"_s : QString())
3815 + convertStored(lhsType, primitive, lhsName)
3816 + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
3819 m_body += m_state.accumulatorVariableOut + u" = "_s;
3820 m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
3824void QQmlJSCodeGenerator::generateCompareOperation(
int lhs,
const QString &cppOperator)
3826 m_body += m_state.accumulatorVariableOut + u" = "_s;
3828 const auto lhsType = registerType(lhs);
3829 const QQmlJSScope::ConstPtr compareType =
3830 m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
3831 ? m_typeResolver->merge(lhsType.storedType(), m_state.accumulatorIn().storedType())
3832 : m_typeResolver->jsPrimitiveType();
3834 m_body += conversion(
3835 m_typeResolver->boolType(), m_state.accumulatorOut(),
3836 convertStored(registerType(lhs).storedType(), compareType,
3837 consumedRegisterVariable(lhs))
3838 + u' ' + cppOperator + u' '
3839 + convertStored(m_state.accumulatorIn().storedType(), compareType,
3840 consumedAccumulatorVariableIn()));
3844void QQmlJSCodeGenerator::generateArithmeticOperation(
int lhs,
const QString &cppOperator)
3846 generateArithmeticOperation(
3847 conversion(registerType(lhs), m_state.readRegister(lhs),
3848 consumedRegisterVariable(lhs)),
3849 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3850 consumedAccumulatorVariableIn()),
3854void QQmlJSCodeGenerator::generateShiftOperation(
int lhs,
const QString &cppOperator)
3856 generateArithmeticOperation(
3857 conversion(registerType(lhs), m_state.readRegister(lhs),
3858 consumedRegisterVariable(lhs)),
3859 u'(' + conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3860 consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
3864void QQmlJSCodeGenerator::generateArithmeticOperation(
3865 const QString &lhs,
const QString &rhs,
const QString &cppOperator)
3867 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhs.isEmpty());
3868 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhs.isEmpty());
3870 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3871 m_body += m_state.accumulatorVariableOut;
3873 const QString explicitCast
3874 = originalOut.isStoredIn(m_typeResolver->stringType())
3875 ? originalOut.storedType()->internalName()
3877 m_body += conversion(
3878 originalOut, m_state.accumulatorOut(),
3879 explicitCast + u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
3883void QQmlJSCodeGenerator::generateArithmeticConstOperation(
int rhsConst,
const QString &cppOperator)
3885 generateArithmeticOperation(
3886 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3887 consumedAccumulatorVariableIn()),
3888 conversion(literalType(m_typeResolver->int32Type()),
3889 m_state.readAccumulator(), QString::number(rhsConst)),
3893void QQmlJSCodeGenerator::generateUnaryOperation(
const QString &cppOperator)
3895 const auto var = conversion(m_state.accumulatorIn(),
3896 originalType(m_state.readAccumulator()),
3897 consumedAccumulatorVariableIn());
3899 if (var == m_state.accumulatorVariableOut) {
3900 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3904 const auto originalResult = originalType(m_state.accumulatorOut());
3905 if (m_state.accumulatorOut() == originalResult) {
3906 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3907 m_body += m_state.accumulatorVariableOut + u" = "_s
3908 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3912 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3913 originalResult, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
3916void QQmlJSCodeGenerator::generateInPlaceOperation(
const QString &cppOperator)
3920 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3921 m_state.accumulatorVariableIn);
3922 if (var == m_state.accumulatorVariableOut) {
3923 m_body += cppOperator + var + u";\n"_s;
3928 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3929 consumedAccumulatorVariableIn());
3931 const auto originalResult = originalType(m_state.accumulatorOut());
3932 if (m_state.accumulatorOut() == originalResult) {
3933 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3934 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3939 m_body += u"auto converted = "_s + var + u";\n"_s;
3940 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3941 originalResult, m_state.accumulatorOut(), u'('
3942 + cppOperator + u"converted)"_s) + u";\n"_s;
3946void QQmlJSCodeGenerator::generateLookup(
const QString &lookup,
const QString &initialization,
3947 const QString &resultPreparation)
3949 m_body += u"#ifndef QT_NO_DEBUG\n"_s;
3950 generateSetInstructionPointer();
3951 m_body += u"#endif\n"_s;
3953 if (!resultPreparation.isEmpty())
3954 m_body += resultPreparation + u";\n"_s;
3955 m_body += u"while (!"_s + lookup + u") {\n"_s;
3957 m_body += u"#ifdef QT_NO_DEBUG\n"_s;
3958 generateSetInstructionPointer();
3959 m_body += u"#endif\n"_s;
3961 m_body += initialization + u";\n"_s;
3962 generateExceptionCheck();
3963 if (!resultPreparation.isEmpty())
3964 m_body += resultPreparation + u";\n"_s;
3968void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset)
3970 QString conversionCode;
3971 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
3972 const auto annotation = m_annotations.find(absoluteOffset);
3973 if (
static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
3974 const auto &conversions = annotation->second.typeConversions;
3976 for (
auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
3977 regIt != regEnd; ++regIt) {
3978 const QQmlJSRegisterContent targetType = regIt.value().content;
3979 if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
3982 const int registerIndex = regIt.key();
3983 const auto variable = m_registerVariables.constFind(targetType);
3985 if (variable == m_registerVariables.constEnd())
3988 QQmlJSRegisterContent currentType;
3989 QString currentVariable;
3990 if (registerIndex == m_state.changedRegisterIndex()) {
3991 currentVariable = changedRegisterVariable();
3992 if (variable->variableName == currentVariable)
3995 currentType = m_state.changedRegister();
3996 currentVariable = u"std::move("_s + currentVariable + u')';
3998 const auto it = m_state.registers.find(registerIndex);
3999 if (it == m_state.registers.end()
4000 || variable->variableName == registerVariable(registerIndex)) {
4004 currentType = it.value().content;
4005 currentVariable = consumedRegisterVariable(registerIndex);
4009 if (currentType == targetType)
4012 conversionCode += variable->variableName;
4013 conversionCode += u" = "_s;
4014 conversionCode += conversion(currentType, targetType, currentVariable);
4015 conversionCode += u";\n"_s;
4019 if (relativeOffset) {
4020 auto labelIt = m_labels.find(absoluteOffset);
4021 if (labelIt == m_labels.end())
4022 labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
4023 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
4026 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
4029QString QQmlJSCodeGenerator::registerVariable(
int index)
const
4031 QQmlJSRegisterContent content = registerType(index);
4032 const auto it = m_registerVariables.constFind(content);
4033 if (it != m_registerVariables.constEnd())
4034 return it->variableName;
4039QString QQmlJSCodeGenerator::lookupVariable(
int lookupIndex)
const
4041 for (
auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
4042 if (it.key().resultLookupIndex() == lookupIndex)
4043 return it->variableName;
4048QString QQmlJSCodeGenerator::consumedRegisterVariable(
int index)
const
4050 const QString var = registerVariable(index);
4051 if (var.isEmpty() || !shouldMoveRegister(index))
4053 return u"std::move(" + var + u")";
4056QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn()
const
4058 return shouldMoveRegister(Accumulator)
4059 ? u"std::move(" + m_state.accumulatorVariableIn + u")"
4060 : m_state.accumulatorVariableIn;
4063QString QQmlJSCodeGenerator::changedRegisterVariable()
const
4065 QQmlJSRegisterContent changedRegister = m_state.changedRegister();
4067 const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
4068 if (storedType.isNull())
4071 return m_registerVariables.value(changedRegister).variableName;
4074QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(
int index)
const
4076 auto it = m_state.registers.find(index);
4077 if (it != m_state.registers.end())
4078 return it.value().content;
4080 return QQmlJSRegisterContent();
4083QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(
int lookupIndex)
const
4085 auto it = m_state.lookups.find(lookupIndex);
4086 if (it != m_state.lookups.end())
4087 return it.value().content;
4089 return QQmlJSRegisterContent();
4092bool QQmlJSCodeGenerator::shouldMoveRegister(
int index)
const
4094 return m_state.canMoveReadRegister(index)
4095 && !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
4098QString QQmlJSCodeGenerator::conversion(
4099 QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4101 const QQmlJSScope::ConstPtr contained = to.containedType();
4104 if (from.isStoredIn(m_typeResolver->jsPrimitiveType()) && m_typeResolver->isPrimitive(to)) {
4106 QString primitive = [&]() -> QString {
4107 if (contained == m_typeResolver->jsPrimitiveType())
4110 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
4111 if (contained == m_typeResolver->boolType())
4112 return conversion.arg(u"Boolean"_s);
4113 if (m_typeResolver->isIntegral(to))
4114 return conversion.arg(u"Integer"_s);
4115 if (m_typeResolver->isNumeric(to))
4116 return conversion.arg(u"Double"_s);
4117 if (contained == m_typeResolver->stringType())
4118 return conversion.arg(u"String"_s);
4120 u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
4123 if (primitive.isEmpty())
4126 return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
4129 if (to.isStoredIn(contained)
4130 || m_typeResolver->isNumeric(to.storedType())
4131 || to.storedType()->isReferenceType()
4132 || from.contains(contained)) {
4139 return convertStored(from.storedType(), to.storedType(), variable);
4141 return convertContained(from, to, variable);
4145QString QQmlJSCodeGenerator::convertStored(
4146 const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
const QString &variable)
4150 Q_ASSERT(!to->isComposite());
4152 const auto jsValueType = m_typeResolver->jsValueType();
4153 const auto varType = m_typeResolver->varType();
4154 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
4155 const auto boolType = m_typeResolver->boolType();
4157 auto zeroBoolOrInt = [&](
const QQmlJSScope::ConstPtr &to) {
4160 if (m_typeResolver->isSignedInteger(to))
4162 if (m_typeResolver->isUnsignedInteger(to))
4167 if (from == m_typeResolver->voidType()) {
4168 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4169 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4170 const QString zero = zeroBoolOrInt(to);
4171 if (!zero.isEmpty())
4173 if (to == m_typeResolver->floatType())
4174 return u"std::numeric_limits<float>::quiet_NaN()"_s;
4175 if (to == m_typeResolver->realType())
4176 return u"std::numeric_limits<double>::quiet_NaN()"_s;
4177 if (to == m_typeResolver->stringType())
4178 return QQmlJSUtils::toLiteral(u"undefined"_s);
4179 if (to == m_typeResolver->varType())
4180 return u"QVariant()"_s;
4181 if (to == m_typeResolver->jsValueType())
4182 return u"QJSValue();"_s;
4183 if (to == m_typeResolver->jsPrimitiveType())
4184 return u"QJSPrimitiveValue()"_s;
4189 if (from == m_typeResolver->nullType()) {
4190 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4191 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4192 if (to == jsValueType)
4193 return u"QJSValue(QJSValue::NullValue)"_s;
4194 if (to == jsPrimitiveType)
4195 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
4197 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
4198 const QString zero = zeroBoolOrInt(to);
4199 if (!zero.isEmpty())
4201 if (to == m_typeResolver->floatType())
4203 if (to == m_typeResolver->realType())
4205 if (to == m_typeResolver->stringType())
4206 return QQmlJSUtils::toLiteral(u"null"_s);
4209 REJECT<QString>(u"Conversion from null to %1"_s.arg(to->internalName()));
4215 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4216 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4221 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
4223 if (base->internalName() == to->internalName())
4224 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4226 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
4227 if (base->internalName() == from->internalName())
4228 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4230 }
else if (to == m_typeResolver->boolType()) {
4231 return u'(' + variable + u" != nullptr)"_s;
4235 auto isJsValue = [&](
const QQmlJSScope::ConstPtr &candidate) {
4236 return candidate == jsValueType || candidate->isScript();
4239 if (isJsValue(from) && isJsValue(to))
4242 const auto isBoolOrNumber = [&](
const QQmlJSScope::ConstPtr &type) {
4243 return m_typeResolver->isNumeric(type)
4244 || type == m_typeResolver->boolType()
4245 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
4248 if (from == m_typeResolver->realType() || from == m_typeResolver->floatType()) {
4249 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type()) {
4250 return to->internalName() + u"(QJSNumberCoercion::roundTowards0("_s
4251 + variable + u"))"_s;
4254 if (m_typeResolver->isSignedInteger(to))
4255 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
4256 if (m_typeResolver->isUnsignedInteger(to))
4257 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
4258 if (to == m_typeResolver->boolType())
4259 return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
4262 if (isBoolOrNumber(from) && isBoolOrNumber(to))
4263 return to->internalName() + u'(' + variable + u')';
4266 if (from == jsPrimitiveType) {
4267 if (to == m_typeResolver->realType())
4268 return variable + u".toDouble()"_s;
4270 return variable + u".toBoolean()"_s;
4271 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type())
4272 return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
4273 if (m_typeResolver->isIntegral(to))
4274 return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
4275 if (to == m_typeResolver->stringType())
4276 return variable + u".toString()"_s;
4277 if (to == jsValueType)
4278 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
4280 return variable + u".toVariant()"_s;
4281 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4282 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4285 if (isJsValue(from)) {
4286 if (to == jsPrimitiveType)
4287 return variable + u".toPrimitive()"_s;
4289 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
4290 return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
4293 if (to == jsPrimitiveType) {
4295 Q_ASSERT(from != m_typeResolver->nullType());
4296 Q_ASSERT(from != m_typeResolver->voidType());
4298 if (from == m_typeResolver->boolType()
4299 || from == m_typeResolver->int32Type()
4300 || from == m_typeResolver->realType()
4301 || from == m_typeResolver->stringType()) {
4302 return u"QJSPrimitiveValue("_s + variable + u')';
4303 }
else if (from == m_typeResolver->int16Type()
4304 || from == m_typeResolver->int8Type()
4305 || from == m_typeResolver->uint16Type()
4306 || from == m_typeResolver->uint8Type()) {
4307 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
4308 }
else if (m_typeResolver->isNumeric(from)) {
4309 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
4313 if (to == jsValueType)
4314 return u"aotContext->engine->toScriptValue("_s + variable + u')';
4316 if (from == varType) {
4317 if (to == m_typeResolver->listPropertyType())
4318 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
4319 return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
4324 return u"QVariant::fromValue("_s + variable + u')';
4326 if (from == m_typeResolver->urlType() && to == m_typeResolver->stringType())
4327 return variable + u".toString()"_s;
4329 if (from == m_typeResolver->stringType() && to == m_typeResolver->urlType())
4330 return u"QUrl("_s + variable + u')';
4332 if (from == m_typeResolver->byteArrayType() && to == m_typeResolver->stringType())
4333 return u"QString::fromUtf8("_s + variable + u')';
4335 if (from == m_typeResolver->stringType() && to == m_typeResolver->byteArrayType())
4336 return variable + u".toUtf8()"_s;
4338 for (
const auto &originType : {
4339 m_typeResolver->dateTimeType(),
4340 m_typeResolver->dateType(),
4341 m_typeResolver->timeType()}) {
4342 if (from == originType) {
4343 for (
const auto &targetType : {
4344 m_typeResolver->dateTimeType(),
4345 m_typeResolver->dateType(),
4346 m_typeResolver->timeType(),
4347 m_typeResolver->stringType(),
4348 m_typeResolver->realType()}) {
4349 if (to == targetType) {
4350 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
4351 originType->internalName(), targetType->internalName(), variable);
4358 const auto retrieveFromPrimitive = [&](
4359 const QQmlJSScope::ConstPtr &type,
const QString &expression) -> QString
4361 if (type == m_typeResolver->boolType())
4362 return expression + u".toBoolean()"_s;
4363 if (m_typeResolver->isSignedInteger(type))
4364 return expression + u".toInteger()"_s;
4365 if (m_typeResolver->isUnsignedInteger(type))
4366 return u"uint("_s + expression + u".toInteger())"_s;
4367 if (type == m_typeResolver->realType())
4368 return expression + u".toDouble()"_s;
4369 if (type == m_typeResolver->floatType())
4370 return u"float("_s + expression + u".toDouble())"_s;
4371 if (type == m_typeResolver->stringType())
4372 return expression + u".toString()"_s;
4376 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
4377 const QString retrieve = retrieveFromPrimitive(
4378 to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
4379 if (!retrieve.isEmpty())
4383 if (from->isReferenceType() && to == m_typeResolver->stringType()) {
4384 return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
4385 + castTargetName(to) + u">("_s + variable + u')';
4389 if (to == m_typeResolver->boolType()) {
4391 Q_ASSERT(from != m_typeResolver->nullType());
4392 Q_ASSERT(from != m_typeResolver->voidType());
4393 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
4394 Q_ASSERT(!isBoolOrNumber(from));
4399 if (m_typeResolver->areEquivalentLists(from, to))
4402 if (from->isListProperty()
4403 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
4404 && to->elementType()->isReferenceType()
4405 && !to->isListProperty()) {
4406 return variable + u".toList<"_s + to->internalName() + u">()"_s;
4409 bool isExtension =
false;
4410 if (m_typeResolver->canPopulate(to, from, &isExtension)) {
4412 u"populating "_s + to->internalName() + u" from "_s + from->internalName());
4413 }
else if (
const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
4415 const auto argumentTypes = ctor.parameters();
4416 return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
4417 + u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
4420 if (to == m_typeResolver->stringType()
4421 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
4422 addInclude(u"QtQml/qjslist.h"_s);
4426 return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
4433 u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
4436QString QQmlJSCodeGenerator::convertContained(QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4438 const QQmlJSScope::ConstPtr containedFrom = from.containedType();
4439 const QQmlJSScope::ConstPtr containedTo = to.containedType();
4442 Q_ASSERT(!to.storedType()->isReferenceType());
4443 Q_ASSERT(!to.isStoredIn(containedTo));
4444 Q_ASSERT(containedFrom != containedTo);
4446 if (!to.isStoredIn(m_typeResolver->varType())
4447 && !to.isStoredIn(m_typeResolver->jsPrimitiveType())) {
4448 REJECT<QString>(u"internal conversion into unsupported wrapper type."_s);
4451 bool isExtension =
false;
4452 if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
4453 REJECT<QString>(u"populating "_s + containedTo->internalName()
4454 + u" from "_s + containedFrom->internalName());
4455 }
else if (
const auto ctor = m_typeResolver->selectConstructor(
4456 containedTo, containedFrom, &isExtension); ctor.isValid()) {
4457 return generateCallConstructor(
4458 ctor, {from}, {variable}, metaType(containedTo),
4459 metaObject(isExtension ? containedTo->extensionType().scope : containedTo));
4462 const auto originalFrom = originalType(from);
4463 const auto containedOriginalFrom = originalFrom.containedType();
4464 if (containedFrom != containedOriginalFrom
4465 && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
4468 return conversion(m_pool->storedIn(originalFrom, from.storedType()), to, variable);
4471 if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
4472 const QQmlJSRegisterContent intermediate
4473 = m_pool->storedIn(from, m_typeResolver->jsPrimitiveType());
4474 return conversion(intermediate, to, conversion(from, intermediate, variable));
4478 u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
4479 .arg(from.descriptiveName(), to.descriptiveName()));
4482void QQmlJSCodeGenerator::reject(
const QString &thing)
4484 addError(u"Cannot generate efficient code for %1"_s.arg(thing));
4488void QQmlJSCodeGenerator::skip(
const QString &thing)
4490 addSkip(u"Skipped code generation for %1"_s.arg(thing));
4493QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
4494 : accumulatorOut(generator->m_state.accumulatorOut())
4495 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
4496 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
4497 , generator(generator)
4499 if (accumulatorVariableOut.isEmpty())
4502 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
4503 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
4504 const QQmlJSRegisterContent storage = accumulatorOut.storage();
4505 const QQmlJSScope::ConstPtr stored = storage.containedType();
4506 const QQmlJSScope::ConstPtr origStored = resolver->original(storage).containedType();
4511 if (origStored != stored
4512 || (origContained != accumulatorOut.containedType() && stored == resolver->varType())) {
4514 const bool storable = isTypeStorable(resolver, origStored);
4515 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
4516 generator->m_state.setRegister(Accumulator, generator->originalType(accumulatorOut));
4517 generator->m_body += u"{\n"_s;
4519 generator->m_body += origStored->augmentedInternalName() + u' '
4520 + generator->m_state.accumulatorVariableOut + u";\n";
4522 }
else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
4523 && generator->m_state.readsRegister(Accumulator)
4524 && generator->m_state.accumulatorOut().isStoredIn(resolver->varType())) {
4528 generator->m_state.accumulatorVariableIn
4529 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
4530 generator->m_body += u"{\n"_s;
4531 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
4532 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
4536QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
4538 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
4539 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
4540 generator->m_state.accumulatorOut(), accumulatorOut,
4541 u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
4542 generator->m_body += u"}\n"_s;
4543 generator->m_state.setRegister(Accumulator, accumulatorOut);
4544 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
4545 }
else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
4546 generator->m_body += u"}\n"_s;
4547 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
static QString toNumericString(double value)
static QString messageTypeForMethod(const QString &method)
static QString derefContentPointer(const QString &contentPointer)
#define BYTECODE_UNIMPLEMENTED()
#define INJECT_TRACE_INFO(function)
static QString registerName(int registerIndex, int offset)
static QString minExpression(int argc)
static QString maxExpression(int argc)
static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)