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;
159 result.signature = u" Q_UNUSED(contextUnit);\n Q_UNUSED(argTypes);\n"_s;
163 const auto addVariable = [&](
int registerIndex,
int offset, QQmlJSRegisterContent content) {
164 const QQmlJSScope::ConstPtr seenType = content.storedType();
166 if (!isTypeStorable(m_typeResolver, seenType))
169 const auto oldSize = m_registerVariables.size();
170 auto &e = m_registerVariables[content];
171 if (m_registerVariables.size() != oldSize) {
172 e.variableName = registerName(registerIndex, offset);
173 e.storedType = seenType;
174 e.initialRegisterIndex = std::abs(registerIndex);
176 e.variableName += u'_' + registerName(registerIndex, offset).mid(2);
182QT_WARNING_DISABLE_CLANG(
"-Wrange-loop-analysis")
183 for (
auto it = m_annotations.constBegin(), iend = m_annotations.constEnd(); it != iend; ++it) {
184 const int registerIndex = it->second.changedRegisterIndex;
185 if (registerIndex != InvalidRegister)
186 addVariable(registerIndex, it.key(), it->second.changedRegister);
187 for (
auto jt = it->second.typeConversions.constBegin(),
188 jend = it->second.typeConversions.constEnd();
191 const int registerIndex = jt.key();
192 if (registerIndex != InvalidRegister)
193 addVariable(-registerIndex, it.key(), jt.value().content);
199 for (
const auto loopLabel : m_context->labelInfo)
200 m_labels.insert(loopLabel, u"label_%1"_s.arg(m_labels.size()));
205 m_state.State::operator=(initialState(m_function));
207 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Temporary);
208 const QByteArray byteCode = function->code;
209 decode(byteCode.constData(),
static_cast<uint>(byteCode.size()));
210 m_pool->setAllocationMode(QQmlJSRegisterContentPool::Permanent);
212 QQmlJSAotFunction result;
213 result.includes.swap(m_includes);
215 if (basicBlocksValidationFailed) {
216 result.code +=
"// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
217 "with basic blocks validation but compiled without it.\n"_L1;
220 result.code += u"// %1 at line %2, column %3\n"_s
221 .arg(m_context->name).arg(m_context->line).arg(m_context->column);
223 QStringList initializations;
224 QStringList markings;
225 for (
auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
226 registerIt != registerEnd; ++registerIt) {
229 QString declarationName = registerIt->variableName.mid(2);
231 const int registerIndex = registerIt->initialRegisterIndex;
232 const bool registerIsArgument = isArgument(registerIndex);
234 QString code = registerIt->storedType->internalName();
236 const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
238 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
244 if (!registerIsArgument
245 && registerIndex != Accumulator
246 && registerIndex != This
247 && !function->registerTypes[registerIndex - firstRegisterIndex()].contains(
248 m_typeResolver->voidType())) {
249 code += declarationName + u" = "_s;
250 code += convertStored(m_typeResolver->voidType(), storedType, QString());
251 }
else if (registerIsArgument && argumentType(registerIndex).isStoredIn(storedType)) {
252 const int argumentIndex = registerIndex - FirstArgument;
253 const QQmlJSRegisterContent argument
254 = m_function->argumentTypes[argumentIndex];
255 const QQmlJSRegisterContent originalArgument = originalType(argument);
257 const bool needsConversion = argument != originalArgument;
258 if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
268 code += declarationName + u" = "_s;
270 const auto originalContained = m_typeResolver->originalContainedType(argument);
271 QString originalValue;
272 const bool needsQVariantWrapping =
273 storedType->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence
274 && !originalContained->isReferenceType()
275 && storedType == m_typeResolver->varType()
276 && originalContained != m_typeResolver->varType();
277 if (needsQVariantWrapping) {
278 originalValue = u"QVariant(%1, argv[%2])"_s.arg(metaTypeFromName(originalContained))
279 .arg(QString::number(argumentIndex + 1));
281 originalValue = u"(*static_cast<"_s + castTargetName(originalArgument.storedType())
282 + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
286 code += conversion(originalArgument, argument, originalValue);
288 code += originalValue;
289 }
else if (isPointer) {
290 code += declarationName + u" = nullptr"_s;
292 code += declarationName;
296 initializations.push_back(std::move(code));
299 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
300 }
else if (storedType == m_typeResolver->varType()) {
301 markings.append(u" aotContext->mark("_s + declarationName + u", markStack);\n");
302 }
else if (storedType == m_typeResolver->listPropertyType()) {
304 }
else if (storedType == m_typeResolver->variantMapType()
305 || storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
306 QString marking = u" for (const auto &v : std::as_const(" + declarationName + u"))\n"
307 + u" aotContext->mark(v, markStack);\n";
308 markings.append(std::move(marking));
312 result.code += u"struct Storage : QQmlPrivate::AOTTrackedLocalsStorage {\n"_s;
313 result.code += u"Storage(const QQmlPrivate::AOTCompiledContext *ctxt, void **a)"_s;
314 result.code += u" : aotContext(ctxt), argv(a) {}\n"_s;
315 result.code += u"void markObjects(QV4::MarkStack *markStack) const final {"_s;
316 result.code += u" Q_UNUSED(markStack);\n"_s;
319 for (
const QString &marking : std::as_const(markings))
320 result.code += marking;
322 result.code += u"}\n"_s;
323 result.code += u"const QQmlPrivate::AOTCompiledContext *aotContext;\n"_s;
324 result.code += u"void **argv;\n"_s;
327 initializations.sort();
328 for (
const QString &initialization : std::as_const(initializations))
329 result.code += initialization;
331 result.code += u"};\nStorage s(aotContext, argv);\n"_s;
332 result.code += u"aotContext->setLocals(&s);\n"_s;
334 result.code += m_body;
338 = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { contextUnit };\n"
339 " const auto *aotContext = &c;\n"
340 " Q_UNUSED(aotContext);\n"_s;
342 if (function->returnType.isValid()) {
343 signature += u" argTypes[0] = %1;\n"_s.arg(
344 metaType(function->returnType.containedType()));
346 signature += u" argTypes[0] = QMetaType();\n"_s;
348 result.numArguments = function->argumentTypes.length();
349 for (qsizetype i = 0; i != result.numArguments; ++i) {
350 signature += u" argTypes[%1] = %2;\n"_s.arg(
351 QString::number(i + 1),
352 metaType(m_typeResolver->originalContainedType(function->argumentTypes[i])));
355 result.signature = std::move(signature);
359void QQmlJSCodeGenerator::generateReturnError()
361 const auto finalizeReturn = qScopeGuard([
this]() { m_body += u"return;\n"_s; });
363 m_body += u"aotContext->setReturnValueUndefined();\n"_s;
364 const auto ret = m_function->returnType;
365 if (!ret.isValid() || ret.contains(m_typeResolver->voidType()))
368 m_body += u"if (argv[0]) {\n"_s;
370 const auto contained = ret.containedType();
371 const auto stored = ret.storedType();
372 if (contained->isReferenceType() && stored->isReferenceType()) {
373 m_body += u" *static_cast<"_s
374 + stored->augmentedInternalName()
375 + u" *>(argv[0]) = nullptr;\n"_s;
376 }
else if (contained == stored) {
377 m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
378 + stored->internalName() + u"();\n"_s;
380 m_body += u" const QMetaType returnType = "_s
381 + metaType(ret.containedType()) + u";\n"_s;
382 m_body += u" returnType.destruct(argv[0]);\n"_s;
383 m_body += u" returnType.construct(argv[0]);\n "_s;
389void QQmlJSCodeGenerator::generate_Ret()
393 const auto finalizeReturn = qScopeGuard([
this]() {
394 m_body += u"return;\n"_s;
395 m_skipUntilNextLabel =
true;
399 if (!m_function->returnType.isValid())
402 m_body += u"if (argv[0]) {\n"_s;
404 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
405 const QString in = m_state.accumulatorVariableIn;
407 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
410 if (accumulatorIn.isStoredIn(m_typeResolver->voidType()))
411 m_body += signalUndefined;
412 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
413 m_body += u" if (!"_s + in + u".isValid())\n"_s;
414 m_body += u" "_s + signalUndefined;
415 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
416 m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
417 m_body += u" "_s + signalUndefined;
418 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
419 m_body += u" if ("_s + in + u".isUndefined())\n"_s;
420 m_body += u" "_s + signalUndefined;
423 if (m_function->returnType.contains(m_typeResolver->voidType())) {
428 const auto contained = m_function->returnType.containedType();
429 const auto stored = m_function->returnType.storedType();
430 if (contained == stored || (contained->isReferenceType() && stored->isReferenceType())) {
433 m_body += u" *static_cast<"_s
434 + stored->augmentedInternalName()
435 + u" *>(argv[0]) = "_s
437 accumulatorIn, m_function->returnType,
438 m_typeResolver->isTriviallyCopyable(accumulatorIn.storedType())
440 : u"std::move("_s + in + u')')
442 }
else if (accumulatorIn.contains(contained)) {
443 m_body += u" const QMetaType returnType = "_s + contentType(accumulatorIn, in)
445 m_body += u" returnType.destruct(argv[0]);\n"_s;
446 m_body += u" returnType.construct(argv[0], "_s
447 + contentPointer(accumulatorIn, in) + u");\n"_s;
449 m_body += u" const auto converted = "_s
450 + conversion(accumulatorIn, m_function->returnType,
451 consumedAccumulatorVariableIn()) + u";\n"_s;
452 m_body += u" const QMetaType returnType = "_s
453 + contentType(m_function->returnType, u"converted"_s)
455 m_body += u" returnType.destruct(argv[0]);\n"_s;
456 m_body += u" returnType.construct(argv[0], "_s
457 + contentPointer(m_function->returnType, u"converted"_s) + u");\n"_s;
463void QQmlJSCodeGenerator::generate_Debug()
470 if (value >= std::numeric_limits<
int>::min() && value <= std::numeric_limits<
int>::max()) {
473 return QString::number(i);
476 switch (qFpClassify(value)) {
478 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
479 return std::signbit(value) ? (u'-' + inf) : inf;
482 return u"std::numeric_limits<double>::quiet_NaN()"_s;
484 return std::signbit(value) ? u"-0.0"_s : u"0"_s;
489 return QString::number(value,
'f', std::numeric_limits<
double>::max_digits10);
492void QQmlJSCodeGenerator::generate_LoadConst(
int index)
500 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(index);
501 const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(encodedConst);
502 const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(encodedConst);
504 m_body += m_state.accumulatorVariableOut + u" = "_s;
505 if (type == m_typeResolver->realType()) {
506 m_body += conversion(
507 type, m_state.accumulatorOut(),
508 toNumericString(value.doubleValue()));
509 }
else if (type == m_typeResolver->int32Type()) {
510 m_body += conversion(
511 type, m_state.accumulatorOut(),
512 QString::number(value.integerValue()));
513 }
else if (type == m_typeResolver->boolType()) {
514 m_body += conversion(
515 type, m_state.accumulatorOut(),
516 value.booleanValue() ? u"true"_s : u"false"_s);
517 }
else if (type == m_typeResolver->voidType()) {
518 m_body += conversion(
519 type, m_state.accumulatorOut(),
521 }
else if (type == m_typeResolver->nullType()) {
522 m_body += conversion(
523 type, m_state.accumulatorOut(),
526 REJECT(u"unsupported constant type"_s);
532void QQmlJSCodeGenerator::generate_LoadZero()
536 m_body += m_state.accumulatorVariableOut;
537 m_body += u" = "_s + conversion(
538 m_typeResolver->int32Type(), m_state.accumulatorOut(), u"0"_s);
542void QQmlJSCodeGenerator::generate_LoadTrue()
546 m_body += m_state.accumulatorVariableOut;
547 m_body += u" = "_s + conversion(
548 m_typeResolver->boolType(), m_state.accumulatorOut(), u"true"_s);
552void QQmlJSCodeGenerator::generate_LoadFalse()
556 m_body += m_state.accumulatorVariableOut;
557 m_body += u" = "_s + conversion(
558 m_typeResolver->boolType(), m_state.accumulatorOut(), u"false"_s);
562void QQmlJSCodeGenerator::generate_LoadNull()
566 m_body += m_state.accumulatorVariableOut + u" = "_s;
567 m_body += conversion(m_typeResolver->nullType(), m_state.accumulatorOut(),
572void QQmlJSCodeGenerator::generate_LoadUndefined()
576 m_body += m_state.accumulatorVariableOut + u" = "_s;
577 m_body += conversion(m_typeResolver->voidType(), m_state.accumulatorOut(),
582void QQmlJSCodeGenerator::generate_LoadInt(
int value)
586 m_body += m_state.accumulatorVariableOut;
588 m_body += conversion(m_typeResolver->int32Type(), m_state.accumulatorOut(),
589 QString::number(value));
593void QQmlJSCodeGenerator::generate_MoveConst(
int constIndex,
int destTemp)
597 Q_ASSERT(destTemp == m_state.changedRegisterIndex());
599 auto var = changedRegisterVariable();
603 const auto v4Value = QV4::StaticValue::fromReturnedValue(
604 m_jsUnitGenerator->constant(constIndex));
606 const auto changed = m_state.changedRegister();
607 QQmlJSScope::ConstPtr contained;
610 m_body += var + u" = "_s;
611 if (v4Value.isNull()) {
612 contained = m_typeResolver->nullType();
613 }
else if (v4Value.isUndefined()) {
614 contained = m_typeResolver->voidType();
615 }
else if (v4Value.isBoolean()) {
616 contained = m_typeResolver->boolType();
617 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
618 }
else if (v4Value.isInteger()) {
619 contained = m_typeResolver->int32Type();
620 input = QString::number(v4Value.int_32());
621 }
else if (v4Value.isDouble()) {
622 contained = m_typeResolver->realType();
623 input = toNumericString(v4Value.doubleValue());
625 REJECT(u"unknown const type"_s);
627 m_body += conversion(contained, changed, input) + u";\n"_s;
630void QQmlJSCodeGenerator::generate_LoadReg(
int reg)
634 if (registerVariable(reg) == m_state.accumulatorVariableOut)
637 m_body += m_state.accumulatorVariableOut;
639 m_body += conversion(
640 registerType(reg), m_state.accumulatorOut(), consumedRegisterVariable(reg));
644void QQmlJSCodeGenerator::generate_StoreReg(
int reg)
648 Q_ASSERT(m_state.changedRegisterIndex() == reg);
649 Q_ASSERT(m_state.accumulatorIn().isValid());
650 const QString var = changedRegisterVariable();
653 if (var == m_state.accumulatorVariableIn)
657 m_body += conversion(m_state.accumulatorIn(), m_state.changedRegister(),
658 consumedAccumulatorVariableIn());
662void QQmlJSCodeGenerator::generate_MoveReg(
int srcReg,
int destReg)
666 Q_ASSERT(m_state.changedRegisterIndex() == destReg);
667 const QString destRegName = changedRegisterVariable();
668 if (destRegName.isEmpty())
670 if (destRegName == registerVariable(srcReg))
672 m_body += destRegName;
674 m_body += conversion(
675 registerType(srcReg), m_state.changedRegister(), consumedRegisterVariable(srcReg));
679void QQmlJSCodeGenerator::generate_LoadImport(
int index)
685void QQmlJSCodeGenerator::generate_LoadLocal(
int index)
691void QQmlJSCodeGenerator::generate_StoreLocal(
int index)
697void QQmlJSCodeGenerator::generate_LoadScopedLocal(
int scope,
int index)
704void QQmlJSCodeGenerator::generate_StoreScopedLocal(
int scope,
int index)
711void QQmlJSCodeGenerator::generate_LoadRuntimeString(
int stringId)
715 m_body += m_state.accumulatorVariableOut;
717 m_body += conversion(m_typeResolver->stringType(), m_state.accumulatorOut(),
718 QQmlJSUtils::toLiteral(m_jsUnitGenerator->stringForIndex(stringId)));
722void QQmlJSCodeGenerator::generate_MoveRegExp(
int regExpId,
int destReg)
729void QQmlJSCodeGenerator::generate_LoadClosure(
int value)
735void QQmlJSCodeGenerator::generate_LoadName(
int nameIndex)
741void QQmlJSCodeGenerator::generate_LoadGlobalLookup(
int index)
745 AccumulatorConverter registers(
this);
747 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index) + u", "_s
748 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
749 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
750 + QString::number(index) + u", "_s
751 + contentType(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
752 const QString preparation = getLookupPreparation(
753 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
754 generateLookup(lookup, initialization, preparation);
757void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(
int index)
761 AccumulatorConverter registers(
this);
763 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
764 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
767 m_body += m_state.accumulatorVariableOut + u" = "_s
769 m_typeResolver->jsValueType(), m_state.accumulatorOut(),
770 u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
775 const QString indexString = QString::number(index);
776 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
777 const QString lookup = u"aotContext->loadContextIdLookup("_s
778 + indexString + u", "_s
779 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
780 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
781 + indexString + u')';
782 generateLookup(lookup, initialization);
786 const bool isProperty = m_state.accumulatorOut().isProperty();
787 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
789 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
790 + indexString + u", "_s
791 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
792 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
793 + indexString + u')';
794 const QString preparation = getLookupPreparation(
795 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
797 generateLookup(lookup, initialization, preparation);
798 }
else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
799 generateTypeLookup(index);
801 REJECT(u"lookup of %1"_s.arg(m_state.accumulatorOut().descriptiveName()));
805void QQmlJSCodeGenerator::generate_StoreNameSloppy(
int nameIndex)
809 const QString name = m_jsUnitGenerator->stringForIndex(nameIndex);
810 const QQmlJSRegisterContent type = m_typeResolver->scopedType(m_function->qmlScope, name);
811 Q_ASSERT(type.isProperty());
813 switch (type.variant()) {
814 case QQmlJSRegisterContent::Property: {
817 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
819 + contentPointer(m_state.accumulatorIn(), m_state.accumulatorVariableIn)
821 + contentType(m_state.accumulatorIn(), m_state.accumulatorVariableIn) + u')';
825 case QQmlJSRegisterContent::Method:
826 REJECT(u"assignment to scope method"_s);
832void QQmlJSCodeGenerator::generate_StoreNameStrict(
int name)
838void QQmlJSCodeGenerator::generate_LoadElement(
int base)
842 const QQmlJSRegisterContent baseType = registerType(base);
844 if (!baseType.isList() && !baseType.isStoredIn(m_typeResolver->stringType()))
845 REJECT(u"LoadElement with non-list base type "_s + baseType.descriptiveName());
847 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
848 conversion(literalType(m_typeResolver->voidType()),
849 m_state.accumulatorOut(), QString()) + u";\n"_s;
851 AccumulatorConverter registers(
this);
853 QString indexName = m_state.accumulatorVariableIn;
854 QQmlJSScope::ConstPtr indexType;
855 if (m_typeResolver->isNumeric(m_state.accumulatorIn())) {
856 indexType = m_state.accumulatorIn().containedType();
857 }
else if (m_state.accumulatorIn().isConversion()) {
858 const auto target = m_typeResolver->extractNonVoidFromOptionalType(m_state.accumulatorIn());
859 if (m_typeResolver->isNumeric(target)) {
860 indexType = target.containedType();
861 m_body += u"if (!" + indexName + u".metaType().isValid())\n"
864 indexName = convertStored(
865 m_state.accumulatorIn().storedType(), indexType, indexName);
867 REJECT(u"LoadElement with non-numeric argument"_s);
871 const QString baseName = registerVariable(base);
873 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
874 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
877 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
878 m_body += u"if ("_s + indexName + u" < 0)\n"_s
883 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
885 m_body += u"if ("_s + indexName + u" < "_s + baseName
886 + u".count(&"_s + baseName + u"))\n"_s;
887 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
888 conversion(m_typeResolver->qObjectType(), m_state.accumulatorOut(),
889 baseName + u".at(&"_s + baseName + u", "_s
890 + indexName + u')') + u";\n"_s;
891 m_body += u"else\n"_s
897 QQmlJSRegisterContent elementType = m_typeResolver->elementType(baseType);
898 elementType = m_pool->storedIn(
899 elementType, m_typeResolver->storedType(elementType.containedType()));
901 QString access = baseName + u".at("_s + indexName + u')';
904 if (baseType.isStoredIn(m_typeResolver->stringType()))
905 access = u"QString("_s + access + u")"_s;
906 else if (isRegisterAffectedBySideEffects(base))
907 REJECT(u"LoadElement on a sequence potentially affected by side effects"_s);
908 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
909 REJECT(u"LoadElement on a sequence wrapped in a non-sequence type"_s);
911 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
912 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
913 conversion(elementType, m_state.accumulatorOut(), access) + u";\n"_s;
914 m_body += u"else\n"_s
918void QQmlJSCodeGenerator::generate_StoreElement(
int base,
int index)
922 const QQmlJSRegisterContent baseType = registerType(base);
923 const QQmlJSScope::ConstPtr indexType = registerType(index).containedType();
925 if (!m_typeResolver->isNumeric(indexType) || !baseType.isList())
926 REJECT(u"StoreElement with non-list base type or non-numeric arguments"_s);
928 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
929 REJECT(u"indirect StoreElement"_s);
931 const QString baseName = registerVariable(base);
932 const QString indexName = registerVariable(index);
934 const auto elementType = m_typeResolver->genericType(
935 m_typeResolver->elementType(baseType).containedType());
937 addInclude(u"QtQml/qjslist.h"_s);
938 if (!m_typeResolver->isNativeArrayIndex(indexType))
939 m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
940 else if (!m_typeResolver->isUnsignedInteger(indexType))
941 m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
945 if (baseType.isStoredIn(m_typeResolver->listPropertyType())) {
946 m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
948 m_body += u" "_s + baseName + u".replace(&"_s + baseName
949 + u", "_s + indexName + u", "_s;
950 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
956 if (isRegisterAffectedBySideEffects(base))
957 REJECT(u"StoreElement on a sequence potentially affected by side effects"_s);
958 if (isRegisterAffectedBySideEffects(Accumulator))
959 REJECT(u"StoreElement of a value potentially affected by side effects"_s);
961 m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
962 m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
963 + indexName + u" + 1);\n"_s;
964 m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
965 m_body += conversion(m_state.accumulatorIn(), elementType, m_state.accumulatorVariableIn)
969 generateWriteBack(base);
972void QQmlJSCodeGenerator::generate_LoadProperty(
int nameIndex)
975 REJECT(u"LoadProperty"_s);
978void QQmlJSCodeGenerator::generate_LoadOptionalProperty(
int name,
int offset)
985void QQmlJSCodeGenerator::generateEnumLookup(
int index)
987 const QString enumMember = m_state.accumulatorOut().enumMember();
994 if (enumMember.isEmpty())
995 REJECT(u"Lookup of enum metatype"_s);
998 const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
999 if (metaEnum.hasValues()) {
1000 m_body += m_state.accumulatorVariableOut + u" = "_s
1001 + QString::number(metaEnum.value(enumMember));
1006 const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
1009 Q_ASSERT(!scopeType->isComposite());
1011 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
1012 if (enumName.isEmpty()) {
1013 if (metaEnum.isFlag() && !metaEnum.name().isEmpty()) {
1014 REJECT(u"qmltypes misses name entry for flag; "
1015 "did you pass the enum type to Q_FLAG instead of the QFlag type?"
1016 "\nType is %1, enum name is %2"_s.arg(scopeType->internalName(), metaEnum.name()));
1018 REJECT(u"qmltypes misses name entry for enum"_s);
1020 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
1021 + u", &"_s + m_state.accumulatorVariableOut + u')';
1022 const QString initialization = u"aotContext->initGetEnumLookup("_s
1023 + QString::number(index) + u", "_s + metaObject(scopeType)
1024 + u", \""_s + enumName + u"\", \""_s + enumMember
1026 generateLookup(lookup, initialization);
1029void QQmlJSCodeGenerator::generateTypeLookup(
int index)
1031 const QString indexString = QString::number(index);
1032 const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(Accumulator).content;
1033 const QString namespaceString
1034 = accumulatorIn.isImportNamespace()
1035 ? QString::number(accumulatorIn.importNamespace())
1036 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1038 switch (m_state.accumulatorOut().variant()) {
1039 case QQmlJSRegisterContent::Singleton: {
1040 rejectIfNonQObjectOut(u"non-QObject singleton type"_s);
1041 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
1042 + u", &"_s + m_state.accumulatorVariableOut + u')';
1043 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
1044 + u", "_s + namespaceString + u')';
1045 generateLookup(lookup, initialization);
1048 case QQmlJSRegisterContent::ModulePrefix:
1050 case QQmlJSRegisterContent::Attachment: {
1051 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1052 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1053 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
1054 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
1055 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
1056 generateLookup(lookup, initialization);
1059 case QQmlJSRegisterContent::Script:
1060 REJECT(u"script lookup"_s);
1061 case QQmlJSRegisterContent::MetaType: {
1062 if (!m_state.accumulatorOut().isStoredIn(m_typeResolver->metaObjectType())) {
1065 REJECT(u"meta-object stored in different type"_s);
1067 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
1068 + u", &"_s + m_state.accumulatorVariableOut + u')';
1069 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
1070 + u", "_s + namespaceString + u")"_s;
1071 generateLookup(lookup, initialization);
1079void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1080 QQmlJSRegisterContent nonStorableContent,
const QString ®isterName,
bool invert)
1082 const auto nonStorableType = nonStorableContent.containedType();
1083 QQmlJSScope::ConstPtr comparedType = (nonStorableType == m_typeResolver->nullType())
1084 ? m_typeResolver->nullType()
1085 : m_typeResolver->voidType();
1088 m_body += u"if ("_s + registerName
1089 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
1090 + m_state.accumulatorVariableOut + u" = "_s
1091 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1092 u"static_cast<const QJSPrimitiveValue *>("_s + registerName
1093 + u".constData())"_s + u"->type() "_s
1094 + (invert ? u"!="_s : u"=="_s)
1095 + (comparedType == m_typeResolver->nullType()
1096 ? u"QJSPrimitiveValue::Null"_s
1097 : u"QJSPrimitiveValue::Undefined"_s))
1098 + u";\n} else if ("_s + registerName
1099 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
1100 + m_state.accumulatorVariableOut + u" = "_s
1101 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1102 (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
1103 + registerName + u".constData())"_s + u"->"_s
1104 + (comparedType == m_typeResolver->nullType()
1106 : u"isUndefined()"_s))
1111 if (nonStorableType == m_typeResolver->nullType()) {
1112 m_body += u"else if ("_s + registerName
1113 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
1114 + m_state.accumulatorVariableOut + u" = "_s
1115 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1116 u"*static_cast<QObject *const *>("_s + registerName
1117 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
1119 + u";\n} else if ("_s + registerName
1120 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
1121 + m_state.accumulatorVariableOut + u" = "_s
1122 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1123 (invert ? u"false"_s : u"true"_s))
1128 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
1129 + conversion(m_typeResolver->boolType(), m_state.accumulatorOut(),
1130 (invert ? (registerName + u".isValid() ? true : false"_s)
1131 : (registerName + u".isValid() ? false : true"_s)))
1135void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1136 QQmlJSRegisterContent storableContent,
const QString &typedRegisterName,
1137 const QString &varRegisterName,
bool invert)
1141 const QQmlJSScope::ConstPtr contained = storableContent.isEnumeration()
1142 ? storableContent.storedType()
1143 : storableContent.containedType();
1145 const QQmlJSScope::ConstPtr boolType = m_typeResolver->boolType();
1146 if (contained->isReferenceType()) {
1147 const QQmlJSScope::ConstPtr comparable = m_typeResolver->qObjectType();
1148 const QString cmpExpr = (invert ? u"!"_s : QString()) + u"(("
1149 + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
1150 + u" && "_s + conversion(storableContent, comparable, typedRegisterName) + u" == "_s
1151 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1153 m_body += m_state.accumulatorVariableOut + u" = "_s
1154 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n";
1158 if (m_typeResolver->isPrimitive(contained)) {
1159 const QQmlJSScope::ConstPtr comparable = m_typeResolver->jsPrimitiveType();
1160 const QString cmpExpr = (invert ? u"!"_s : QString())
1161 + conversion(storableContent, comparable, typedRegisterName)
1162 + u".strictlyEquals("_s
1163 + conversion(m_typeResolver->varType(), comparable, varRegisterName) + u')';
1165 m_body += m_state.accumulatorVariableOut + u" = "_s
1166 + conversion(boolType, m_state.accumulatorOut(), cmpExpr) + u";\n"_s;
1170 REJECT(u"comparison of non-primitive, non-object type to var"_s);
1173void QQmlJSCodeGenerator::generateArrayInitializer(
int argc,
int argv)
1175 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1176 const QQmlJSScope::ConstPtr element = stored->elementType();
1179 QStringList initializer;
1180 for (
int i = 0; i < argc; ++i) {
1181 initializer += convertStored(
1182 registerType(argv + i).storedType(), element,
1183 consumedRegisterVariable(argv + i));
1186 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
1187 m_body += initializer.join(u", "_s);
1191void QQmlJSCodeGenerator::generateWriteBack(
int registerIndex)
1193 QString writeBackRegister = registerVariable(registerIndex);
1194 bool writeBackAffectedBySideEffects = isRegisterAffectedBySideEffects(registerIndex);
1196 for (QQmlJSRegisterContent writeBack = registerType(registerIndex);
1197 !writeBack.storedType()->isReferenceType();) {
1199 switch (writeBack.variant()) {
1200 case QQmlJSRegisterContent::Literal:
1201 case QQmlJSRegisterContent::Operation:
1209 if (writeBackAffectedBySideEffects)
1210 REJECT(u"write-back of value affected by side effects"_s);
1212 if (writeBack.isConversion())
1213 REJECT(u"write-back of converted value"_s);
1215 const int lookupIndex = writeBack.resultLookupIndex();
1226 if (lookupIndex == -1)
1227 REJECT(u"write-back of non-lookup"_s);
1229 const QString writeBackIndexString = QString::number(lookupIndex);
1231 const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
1232 if (variant == QQmlJSRegisterContent::Property && isQmlScopeObject(writeBack.scope())) {
1233 const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
1234 + writeBackIndexString
1235 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1236 const QString initialization = u"aotContext->initLoadScopeObjectPropertyLookup("_s
1237 + writeBackIndexString + u')';
1238 generateLookup(lookup, initialization);
1243 QQmlJSRegisterContent outerContent;
1244 QString outerRegister;
1245 bool outerAffectedBySideEffects =
false;
1246 for (
auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
1248 if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
1249 outerContent = it.value().content;
1250 outerRegister = lookupVariable(outerContent.resultLookupIndex());
1251 outerAffectedBySideEffects = it.value().affectedBySideEffects;
1257 if (!outerContent.isValid())
1258 REJECT(u"write-back of lookup across jumps or merges."_s);
1260 Q_ASSERT(!outerRegister.isEmpty());
1262 switch (writeBack.variant()) {
1263 case QQmlJSRegisterContent::Property:
1264 if (writeBack.scopeType()->isReferenceType()) {
1265 const QString lookup = u"aotContext->writeBackObjectLookup("_s
1266 + writeBackIndexString
1267 + u", "_s + outerRegister
1268 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1270 const QString initialization = (m_state.registers[registerIndex].isShadowable
1271 ? u"aotContext->initGetObjectLookupAsVariant("_s
1272 : u"aotContext->initGetObjectLookup("_s)
1273 + writeBackIndexString + u", "_s + outerRegister + u')';
1275 generateLookup(lookup, initialization);
1277 const QString valuePointer = contentPointer(outerContent, outerRegister);
1278 const QString lookup = u"aotContext->writeBackValueLookup("_s
1279 + writeBackIndexString
1280 + u", "_s + valuePointer
1281 + u", "_s + contentPointer(writeBack, writeBackRegister) + u')';
1282 const QString initialization = u"aotContext->initGetValueLookup("_s
1283 + writeBackIndexString
1284 + u", "_s + metaObject(writeBack.scopeType()) + u')';
1285 generateLookup(lookup, initialization);
1289 REJECT(u"SetLookup on value types (because of missing write-back)"_s);
1292 writeBackRegister = outerRegister;
1293 writeBack = outerContent;
1294 writeBackAffectedBySideEffects = outerAffectedBySideEffects;
1298void QQmlJSCodeGenerator::rejectIfNonQObjectOut(
const QString &error)
1300 if (m_state.accumulatorOut().storedType()->accessSemantics()
1301 != QQmlJSScope::AccessSemantics::Reference) {
1306void QQmlJSCodeGenerator::rejectIfBadArray()
1308 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1309 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
1313 REJECT(u"storing an array in a non-sequence type"_s);
1314 }
else if (stored->isListProperty()) {
1323 REJECT(u"creating a QQmlListProperty not backed by a property"_s);
1329
1330
1331
1332
1333
1334
1335bool QQmlJSCodeGenerator::generateContentPointerCheck(
1336 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1337 const QString &variable,
const QString &errorMessage)
1339 const QQmlJSScope::ConstPtr scope = required;
1340 const QQmlJSScope::ConstPtr input = actual.containedType();
1341 if (QQmlJSUtils::searchBaseAndExtensionTypes(input,
1342 [&](
const QQmlJSScope::ConstPtr &base) {
return base == scope; })) {
1346 if (!m_typeResolver->canHold(input, scope)) {
1347 REJECT<
bool>(u"lookup of members of %1 in %2"_s
1348 .arg(scope->internalName(), input->internalName()));
1351 bool needsVarContentConversion =
false;
1352 QString processedErrorMessage;
1353 if (actual.storedType()->isReferenceType()) {
1357 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
1358 processedErrorMessage = errorMessage.arg(u"null");
1359 }
else if (actual.isStoredIn(m_typeResolver->varType())) {
1363 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
1364 needsVarContentConversion =
true;
1365 processedErrorMessage = errorMessage.arg(u"undefined");
1367 REJECT<
bool>(u"retrieving metatype from %1"_s.arg(actual.descriptiveName()));
1370 generateSetInstructionPointer();
1371 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
1372 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(processedErrorMessage);
1373 generateReturnError();
1375 return needsVarContentConversion;
1378QString QQmlJSCodeGenerator::generateCallConstructor(
1379 const QQmlJSMetaMethod &ctor,
const QList<QQmlJSRegisterContent> &argumentTypes,
1380 const QStringList &arguments,
const QString &metaType,
const QString &metaObject)
1382 const auto parameterTypes = ctor.parameters();
1383 Q_ASSERT(parameterTypes.length() == argumentTypes.length());
1386 QStringList argPointers;
1388 QString result = u"[&](){\n"_s;
1389 for (qsizetype i = 0, end = parameterTypes.length(); i < end; ++i) {
1390 const QQmlJSRegisterContent argumentType = argumentTypes[i];
1391 const QQmlJSScope::ConstPtr parameterType = parameterTypes[i].type();
1392 const QString argument = arguments[i];
1393 const QString arg = u"arg"_s + QString::number(i);
1395 result += u" auto "_s + arg + u" = "_s;
1396 if (argumentType.contains(parameterType)) {
1398 argPointers.append(contentPointer(argumentType, arg));
1400 const QQmlJSRegisterContent parameterTypeConversion
1402 m_typeResolver->convert(argumentType, parameterType),
1403 m_typeResolver->genericType(parameterType));
1404 result += conversion(argumentType, parameterTypeConversion, argument);
1405 argPointers.append(contentPointer(parameterTypeConversion, arg));
1410 result += u" void *args[] = {"_s + argPointers.join(u',') + u"};\n"_s;
1411 result += u" return aotContext->constructValueType("_s + metaType + u", "_s + metaObject
1412 + u", "_s + QString::number(
int(ctor.constructorIndex())) + u", args);\n"_s;
1414 return result + u"}()"_s;
1418 const QQmlJSTypeResolver *typeResolver,
const QQmlJSRegisterContent &baseType)
1420 const QQmlJSScope::ConstPtr contained = baseType.containedType();
1421 switch (contained->accessSemantics()) {
1430 return !typeResolver->isPrimitive(contained);
1433 if (contained->isListProperty())
1436 switch (baseType.variant()) {
1437 case QQmlJSRegisterContent::Operation:
1438 case QQmlJSRegisterContent::Literal: {
1440 const QQmlJSScope::ConstPtr elementContained = contained->elementType();
1441 return !elementContained->isReferenceType()
1442 && !typeResolver->isPrimitive(elementContained);
1452bool QQmlJSCodeGenerator::isRegisterAffectedBySideEffects(
int registerIndex)
1454 if (!m_state.isRegisterAffectedBySideEffects(registerIndex))
1457 QQmlJSRegisterContent baseType = registerType(registerIndex);
1458 if (baseType.isConversion()) {
1462 const auto origins = baseType.conversionOrigins();
1463 for (QQmlJSRegisterContent origin : origins) {
1464 if (canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(origin)))
1471 return canTypeBeAffectedBySideEffects(m_typeResolver, m_typeResolver->original(baseType));
1474QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
1475 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1476 const QString &variable,
const QString &errorMessage)
1478 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1479 return variable + u".data()"_s;
1480 return contentPointer(actual, variable);
1483QString QQmlJSCodeGenerator::resolveQObjectPointer(
1484 const QQmlJSScope::ConstPtr &required, QQmlJSRegisterContent actual,
1485 const QString &variable,
const QString &errorMessage)
1487 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1488 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1492void QQmlJSCodeGenerator::generate_GetLookup(
int index)
1495 generate_GetLookupHelper(index);
1498QString QQmlJSCodeGenerator::generateVariantMapGetLookup(
1499 const QString &map,
const int nameIndex)
1501 const QString mapLookup = map
1502 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1504 return m_state.accumulatorVariableOut + u" = "_s
1505 + conversion(m_typeResolver->varType(), m_state.accumulatorOut(), mapLookup)
1509QString QQmlJSCodeGenerator::generateVariantMapSetLookup(
1510 const QString &map,
const int nameIndex,
1511 const QQmlJSScope::ConstPtr &property,
const QString &variableIn)
1513 const QString mapLookup = map
1514 + u"["_s + QQmlJSUtils::toLiteral(m_jsUnitGenerator->lookupName(nameIndex)) + u"]"_s;
1516 return mapLookup + u" = "_s
1517 + conversion(property, m_typeResolver->varType(), variableIn)
1521void QQmlJSCodeGenerator::generate_GetLookupHelper(
int index)
1523 if (m_state.accumulatorOut().isMethod())
1524 REJECT(u"lookup of function property."_s);
1526 if (m_state.accumulatorOut().scope().contains(m_typeResolver->mathObject())) {
1527 QString name = m_jsUnitGenerator->lookupName(index);
1531 value = std::exp(1.0);
1532 }
else if (name == u"LN10") {
1534 }
else if (name == u"LN2") {
1536 }
else if (name == u"LOG10E") {
1537 value = log10(std::exp(1.0));
1538 }
else if (name == u"LOG2E") {
1539 value = log2(std::exp(1.0));
1540 }
else if (name == u"PI") {
1541 value = 3.14159265358979323846;
1542 }
else if (name == u"SQRT1_2") {
1543 value = std::sqrt(0.5);
1544 }
else if (name == u"SQRT2") {
1545 value = std::sqrt(2.0);
1550 m_body += m_state.accumulatorVariableOut + u" = "_s
1551 + conversion(m_typeResolver->realType(), m_state.accumulatorOut(), toNumericString(value))
1556 if (m_state.accumulatorOut().isImportNamespace()) {
1557 Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ModulePrefix);
1559 if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
1560 m_body += m_state.accumulatorVariableOut + u" = "_s
1561 + conversion(m_state.accumulatorIn(), m_state.accumulatorOut(),
1562 m_state.accumulatorVariableIn)
1568 AccumulatorConverter registers(
this);
1570 if (m_state.accumulatorOut().isEnumeration()) {
1571 generateEnumLookup(index);
1575 const QString indexString = QString::number(index);
1576 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1577 ? QString::number(m_state.accumulatorIn().importNamespace())
1578 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1579 const auto accumulatorIn = m_state.accumulatorIn();
1580 const QQmlJSRegisterContent scope = m_state.accumulatorOut().scope();
1581 const QQmlJSRegisterContent originalScope
1582 = scope.original().isValid() ? scope.original() : scope;
1583 const bool isReferenceType = originalScope.containedType()->isReferenceType();
1585 switch (m_state.accumulatorOut().variant()) {
1586 case QQmlJSRegisterContent::Attachment: {
1587 if (isQmlScopeObject(m_state.accumulatorOut().attachee())) {
1588 generateTypeLookup(index);
1591 if (!isReferenceType) {
1596 REJECT(u"attached object for non-QObject type"_s);
1599 if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
1602 REJECT(u"attached object of potentially non-QObject base"_s);
1605 rejectIfNonQObjectOut(u"non-QObject attached type"_s);
1607 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1608 + u", "_s + m_state.accumulatorVariableIn
1609 + u", &"_s + m_state.accumulatorVariableOut + u')';
1610 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1611 + indexString + u", "_s + namespaceString + u", "_s
1612 + m_state.accumulatorVariableIn + u')';
1613 generateLookup(lookup, initialization);
1616 case QQmlJSRegisterContent::Singleton:
1617 case QQmlJSRegisterContent::Script:
1618 case QQmlJSRegisterContent::MetaType: {
1619 generateTypeLookup(index);
1626 Q_ASSERT(m_state.accumulatorOut().isProperty());
1628 if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1629 REJECT(u"lookup in QJSValue"_s);
1630 }
else if (isReferenceType) {
1631 const QString inputPointer = resolveQObjectPointer(
1632 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1633 u"Cannot read property '%1' of %2"_s.arg(
1634 m_jsUnitGenerator->lookupName(index)));
1635 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1636 + u", "_s + inputPointer + u", "_s
1637 + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut) + u')';
1638 const QString initialization = (m_state.isShadowable()
1639 ? u"aotContext->initGetObjectLookupAsVariant("_s
1640 : u"aotContext->initGetObjectLookup("_s)
1641 + indexString + u", "_s + inputPointer + u')';
1642 const QString preparation = getLookupPreparation(
1643 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1644 generateLookup(lookup, initialization, preparation);
1645 }
else if ((originalScope.containedType()->accessSemantics()
1646 == QQmlJSScope::AccessSemantics::Sequence
1647 || originalScope.contains(m_typeResolver->stringType()))
1648 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1649 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1650 if (stored->isListProperty()) {
1651 m_body += m_state.accumulatorVariableOut + u" = "_s;
1652 m_body += conversion(
1653 originalType(m_state.accumulatorOut()),
1654 m_state.accumulatorOut(),
1655 m_state.accumulatorVariableIn + u".count("_s + u'&'
1656 + m_state.accumulatorVariableIn + u')');
1658 }
else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1659 || stored == m_typeResolver->stringType()) {
1660 m_body += m_state.accumulatorVariableOut + u" = "_s
1661 + conversion(originalType(m_state.accumulatorOut()),
1662 m_state.accumulatorOut(),
1663 m_state.accumulatorVariableIn + u".length()"_s)
1665 }
else if (originalScope.contains(m_typeResolver->stringType())) {
1666 m_body += m_state.accumulatorVariableOut + u" = "_s
1668 m_typeResolver->sizeType(), m_state.accumulatorOut(),
1669 conversion(m_state.accumulatorIn(), m_typeResolver->stringType(),
1670 m_state.accumulatorVariableIn)
1674 REJECT(u"access to 'length' property of sequence wrapped in non-sequence"_s);
1676 }
else if (accumulatorIn.isStoredIn(m_typeResolver->variantMapType())) {
1677 m_body += generateVariantMapGetLookup(m_state.accumulatorVariableIn, index);
1679 if (m_state.isRegisterAffectedBySideEffects(Accumulator))
1680 REJECT(u"reading from a value that's potentially affected by side effects"_s);
1682 const QString inputContentPointer = resolveValueTypeContentPointer(
1683 scope.containedType(), accumulatorIn, m_state.accumulatorVariableIn,
1684 u"Cannot read property '%1' of %2"_s.arg(
1685 m_jsUnitGenerator->lookupName(index)));
1687 if (scope.contains(m_typeResolver->variantMapType())) {
1688 m_body += generateVariantMapGetLookup(
1689 u"(*static_cast<const QVariantMap *>("_s
1690 + inputContentPointer + u"))"_s, index);
1694 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1695 + u", "_s + inputContentPointer
1696 + u", "_s + contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut)
1698 const QString initialization = u"aotContext->initGetValueLookup("_s
1699 + indexString + u", "_s
1700 + metaObject(scope.containedType()) + u')';
1701 const QString preparation = getLookupPreparation(
1702 m_state.accumulatorOut(), m_state.accumulatorVariableOut, index);
1703 generateLookup(lookup, initialization, preparation);
1707void QQmlJSCodeGenerator::generate_GetOptionalLookup(
int index,
int offset)
1711 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
1712 QString accumulatorVarIn = m_state.accumulatorVariableIn;
1714 const auto &annotation = m_annotations[currentInstructionOffset()];
1715 if (accumulatorIn.storedType()->isReferenceType()) {
1716 m_body += u"if (!%1)\n"_s.arg(accumulatorVarIn);
1717 generateJumpCodeWithTypeConversions(offset);
1718 }
else if (accumulatorIn.isStoredIn(m_typeResolver->varType())) {
1719 m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
1720 "&& %1.value<QObject *>() == nullptr))\n"_s.arg(accumulatorVarIn);
1721 generateJumpCodeWithTypeConversions(offset);
1722 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsPrimitiveType())) {
1723 m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
1724 "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(accumulatorVarIn);
1725 generateJumpCodeWithTypeConversions(offset);
1726 }
else if (annotation.changedRegisterIndex == Accumulator
1727 && annotation.changedRegister.variant() == QQmlJSRegisterContent::Enum) {
1729 }
else if (accumulatorIn.isStoredIn(m_typeResolver->jsValueType())) {
1730 m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(accumulatorVarIn);
1731 generateJumpCodeWithTypeConversions(offset);
1732 }
else if (!m_typeResolver->canHoldUndefined(accumulatorIn.storage())) {
1738 generate_GetLookupHelper(index);
1741void QQmlJSCodeGenerator::generate_StoreProperty(
int nameIndex,
int baseReg)
1745 REJECT(u"StoreProperty"_s);
1753 Q_ASSERT(contentPointer.startsWith(u'&') || contentPointer[0].isLetterOrNumber());
1754 return contentPointer.startsWith(u'&') ? contentPointer.mid(1) : (u'*' + contentPointer);
1757void QQmlJSCodeGenerator::generate_SetLookup(
int index,
int baseReg)
1761 const QString indexString = QString::number(index);
1762 const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
1763 const QQmlJSRegisterContent property = m_state.readAccumulator();
1764 Q_ASSERT(property.isConversion());
1765 const QQmlJSRegisterContent original
1766 = m_typeResolver->original(property.conversionResultScope());
1767 const QQmlJSScope::ConstPtr originalScope = original.containedType();
1769 if (property.storedType().isNull()) {
1770 REJECT(u"SetLookup. Could not find property "
1771 + m_jsUnitGenerator->lookupName(index)
1773 + originalScope->internalName());
1776 const QString object = registerVariable(baseReg);
1779 if (!m_state.accumulatorIn().contains(property.containedType())) {
1780 m_body += property.storedType()->augmentedInternalName() + u" converted = "_s
1781 + conversion(m_state.accumulatorIn(), property, consumedAccumulatorVariableIn())
1783 variableIn = contentPointer(property, u"converted"_s);
1785 variableIn = contentPointer(property, m_state.accumulatorVariableIn);
1788 switch (originalScope->accessSemantics()) {
1789 case QQmlJSScope::AccessSemantics::Reference: {
1790 const QString basePointer = resolveQObjectPointer(
1791 originalScope, registerType(baseReg), object,
1792 u"TypeError: Value is %1 and could not be converted to an object"_s);
1794 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1795 + u", "_s + basePointer + u", "_s + variableIn + u')';
1799 const QString initialization
1800 = (property.contains(m_typeResolver->varType())
1801 ? u"aotContext->initSetObjectLookupAsVariant("_s
1802 : u"aotContext->initSetObjectLookup("_s)
1803 + indexString + u", "_s + basePointer + u')';
1804 generateLookup(lookup, initialization);
1807 case QQmlJSScope::AccessSemantics::Sequence: {
1808 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1809 if (propertyName != u"length"_s)
1810 REJECT(u"setting non-length property on a sequence type"_s);
1812 if (!originalScope->isListProperty())
1813 REJECT(u"resizing sequence types (because of missing write-back)"_s);
1816 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1817 m_body += u"const int end = "_s + derefContentPointer(variableIn) + u";\n"_s;
1818 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1819 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1820 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1821 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1825 case QQmlJSScope::AccessSemantics::Value: {
1826 const QQmlJSRegisterContent base = registerType(baseReg);
1827 if (base.isStoredIn(m_typeResolver->variantMapType())) {
1828 m_body += generateVariantMapSetLookup(
1829 registerVariable(baseReg), index, property.storedType(),
1830 derefContentPointer(variableIn));
1831 generateWriteBack(baseReg);
1834 const QString baseContentPointer = resolveValueTypeContentPointer(
1835 originalScope, base, object,
1836 u"TypeError: Value is %1 and could not be converted to an object"_s);
1838 if (original.contains(m_typeResolver->variantMapType())) {
1839 m_body += generateVariantMapSetLookup(
1840 u"(*static_cast<const QVariantMap *>("_s
1841 + baseContentPointer + u"))"_s, index, property.storedType(),
1842 derefContentPointer(variableIn));
1843 generateWriteBack(baseReg);
1847 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1848 + u", "_s + baseContentPointer
1849 + u", "_s + variableIn + u')';
1853 const QString initialization
1854 = (property.contains(m_typeResolver->varType())
1855 ? u"aotContext->initSetValueLookupAsVariant("_s
1856 : u"aotContext->initSetValueLookup("_s)
1857 + indexString + u", "_s + metaObject(originalScope) + u')';
1859 generateLookup(lookup, initialization);
1860 generateWriteBack(baseReg);
1864 case QQmlJSScope::AccessSemantics::None:
1872void QQmlJSCodeGenerator::generate_LoadSuperProperty(
int property)
1878void QQmlJSCodeGenerator::generate_StoreSuperProperty(
int property)
1884void QQmlJSCodeGenerator::generate_Yield()
1889void QQmlJSCodeGenerator::generate_YieldStar()
1894void QQmlJSCodeGenerator::generate_Resume(
int)
1899QString QQmlJSCodeGenerator::initAndCall(
1900 int argc,
int argv,
const QString &callMethodTemplate,
const QString &initMethodTemplate,
1905 if (m_state.changedRegisterIndex() == InvalidRegister ||
1906 m_state.accumulatorOut().contains(m_typeResolver->voidType())) {
1907 args = u"nullptr"_s;
1909 *outVar = u"callResult"_s;
1910 const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
1911 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1914 args = contentPointer(m_state.accumulatorOut(), *outVar);
1920 QString argumentPreparation;
1921 for (
int i = 0; i < argc; ++i) {
1922 if (isRegisterAffectedBySideEffects(argv + i))
1923 REJECT<QString>(u"calling method with argument affected by side effects"_s);
1924 const QQmlJSRegisterContent content = registerType(argv + i);
1925 const QQmlJSRegisterContent read = m_state.readRegister(argv + i);
1926 if (read.contains(content.containedType())) {
1927 args += u", "_s + contentPointer(read, registerVariable(argv + i));
1929 const QString var = u"arg"_s + QString::number(i);
1930 argumentPreparation +=
1931 u" "_s + read.storedType()->augmentedInternalName() + u' ' + var + u" = "_s
1932 + conversion(content, read, consumedRegisterVariable(argv + i)) + u";\n";
1933 args += u", "_s + contentPointer(read, var);
1939 if (m_state.isShadowable()) {
1940 initMethod = initMethodTemplate;
1942 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
1943 Q_ASSERT(!method.isConstructor());
1945 const QQmlJSMetaMethod::RelativeFunctionIndex relativeMethodIndex =
1946 method.isJavaScriptFunction() ? method.jsFunctionIndex() : method.methodIndex();
1947 initMethod = initMethodTemplate.arg(
int(relativeMethodIndex));
1950 return u"const auto doCall = [&]() {\n"_s
1951 + argumentPreparation
1952 + u" void *args[] = {" + args + u"};\n"_s
1953 + u" return aotContext->"_s + callMethodTemplate.arg(u"args"_s).arg(argc) + u";\n"
1955 + u"const auto doInit = [&]() {\n"_s
1956 + u" aotContext->"_s + initMethod + u";\n"
1960void QQmlJSCodeGenerator::generateMoveOutVarAfterCall(
const QString &outVar)
1962 if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
1965 m_body += m_state.accumulatorVariableOut + u" = "_s + u"std::move(" + outVar + u");\n";
1966 m_body += u"aotContext->setImplicitDestructible("_s
1967 + m_state.accumulatorVariableOut + u");\n"_s;
1970void QQmlJSCodeGenerator::generate_CallValue(
int name,
int argc,
int argv)
1978void QQmlJSCodeGenerator::generate_CallWithReceiver(
int name,
int thisObject,
int argc,
int argv)
1981 Q_UNUSED(thisObject)
1987void QQmlJSCodeGenerator::generate_CallProperty(
int nameIndex,
int baseReg,
int argc,
int argv)
1989 Q_UNUSED(nameIndex);
1993 REJECT(u"CallProperty"_s);
1996bool QQmlJSCodeGenerator::inlineStringMethod(
const QString &name,
int base,
int argc,
int argv)
1998 if (name != u"arg"_s || argc != 1)
2001 const auto arg = [&](
const QQmlJSScope::ConstPtr &type) {
2002 return convertStored(registerType(argv).storedType(), type, consumedRegisterVariable(argv));
2005 const auto ret = [&](
const QString &arg) {
2006 const QString expression = convertStored(
2007 registerType(base).storedType(), m_typeResolver->stringType(),
2008 consumedRegisterVariable(base)) + u".arg("_s + arg + u')';
2010 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
2013 const QQmlJSRegisterContent input = m_state.readRegister(argv);
2014 m_body += m_state.accumulatorVariableOut + u" = "_s;
2016 if (m_typeResolver->isNumeric(input))
2017 m_body += ret(arg(input.containedType()));
2018 else if (input.contains(m_typeResolver->boolType()))
2019 m_body += ret(arg(m_typeResolver->boolType()));
2021 m_body += ret(arg(m_typeResolver->stringType()));
2026bool QQmlJSCodeGenerator::inlineTranslateMethod(
const QString &name,
int argc,
int argv)
2028 addInclude(u"QtCore/qcoreapplication.h"_s);
2030 const auto arg = [&](
int i,
const QQmlJSScope::ConstPtr &type) {
2032 return convertStored(registerType(argv + i).storedType(), type,
2033 consumedRegisterVariable(argv + i));
2036 const auto stringArg = [&](
int i) {
2038 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
2042 const auto intArg = [&](
int i) {
2043 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
2046 const auto stringRet = [&](
const QString &expression) {
2048 m_typeResolver->stringType(), m_state.accumulatorOut(), expression);
2051 const auto capture = [&]() {
2052 m_body += u"aotContext->captureTranslation();\n"_s;
2055 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
2057 m_body += m_state.accumulatorVariableOut + u" = "_s
2058 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
2062 if (name == u"QT_TRANSLATE_NOOP"_s) {
2064 m_body += m_state.accumulatorVariableOut + u" = "_s
2065 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
2069 if (name == u"qsTrId"_s) {
2074 m_body += m_state.accumulatorVariableOut + u" = "_s
2075 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
2076 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
2080 if (name == u"qsTr"_s) {
2082 m_body += m_state.accumulatorVariableOut + u" = "_s
2083 + stringRet(u"QCoreApplication::translate("_s
2084 + u"aotContext->translationContext().toUtf8().constData(), "_s
2085 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2086 + intArg(2) + u")"_s) + u";\n"_s;
2090 if (name == u"qsTranslate"_s) {
2092 m_body += m_state.accumulatorVariableOut + u" = "_s
2093 + stringRet(u"QCoreApplication::translate("_s
2094 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
2095 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
2104 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"max() expects at least two arguments.");
2106 QString expression =
2107 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2108 for (
int i = 2; i < argc; i++) {
2110 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
2111 .arg(
"arg"_L1 + QString::number(i + 1));
2113 expression +=
"return tmpMax;\n}()"_L1;
2120 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO,
"min() expects at least two arguments.");
2122 QString expression =
2123 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
2124 for (
int i = 2; i < argc; i++) {
2126 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
2127 .arg(
"arg"_L1 + QString::number(i + 1));
2129 expression +=
"return tmpMin;\n}()"_L1;
2134bool QQmlJSCodeGenerator::inlineMathMethod(
const QString &name,
int argc,
int argv)
2136 addInclude(u"cmath"_s);
2137 addInclude(u"limits"_s);
2138 addInclude(u"QtCore/qalgorithms.h"_s);
2139 addInclude(u"QtCore/qrandom.h"_s);
2140 addInclude(u"QtQml/qjsprimitivevalue.h"_s);
2144 if (m_state.changedRegisterIndex() != Accumulator)
2148 for (
int i = 0; i < argc; ++i) {
2149 m_body += u"const double arg%1 = "_s.arg(i + 1) + convertStored(
2150 registerType(argv + i).storedType(),
2151 m_typeResolver->realType(), consumedRegisterVariable(argv + i))
2155 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
2156 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
2157 m_body += m_state.accumulatorVariableOut + u" = "_s;
2161 if (name == u"abs" && argc == 1) {
2162 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
2163 }
else if (name == u"acos"_s && argc == 1) {
2164 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(qNaN);
2165 }
else if (name == u"acosh"_s && argc == 1) {
2166 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(qNaN);
2167 }
else if (name == u"asin"_s && argc == 1) {
2168 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(qNaN);
2169 }
else if (name == u"asinh"_s && argc == 1) {
2170 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
2171 }
else if (name == u"atan"_s && argc == 1) {
2172 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
2173 }
else if (name == u"atanh"_s && argc == 1) {
2174 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
2175 }
else if (name == u"atan2"_s) {
2178 }
else if (name == u"cbrt"_s && argc == 1) {
2179 expression = u"std::cbrt(arg1)"_s;
2180 }
else if (name == u"ceil"_s && argc == 1) {
2181 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
2182 }
else if (name == u"clz32"_s && argc == 1) {
2183 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
2184 }
else if (name == u"cos"_s && argc == 1) {
2185 expression = u"std::cos(arg1)"_s;
2186 }
else if (name == u"cosh"_s && argc == 1) {
2187 expression = u"std::cosh(arg1)"_s;
2188 }
else if (name == u"exp"_s && argc == 1) {
2189 expression = u"std::isinf(arg1) "
2190 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
2191 ": std::exp(arg1)"_s.arg(inf);
2192 }
else if (name == u"expm1"_s) {
2195 }
else if (name == u"floor"_s && argc == 1) {
2196 expression = u"std::floor(arg1)"_s;
2197 }
else if (name == u"fround"_s && argc == 1) {
2198 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
2200 ": double(float(arg1))"_s;
2201 }
else if (name == u"hypot"_s) {
2204 }
else if (name == u"imul"_s && argc == 2) {
2205 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
2206 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
2207 }
else if (name == u"log"_s && argc == 1) {
2208 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(qNaN);
2209 }
else if (name == u"log10"_s && argc == 1) {
2210 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(qNaN);
2211 }
else if (name == u"log1p"_s && argc == 1) {
2212 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(qNaN);
2213 }
else if (name == u"log2"_s && argc == 1) {
2214 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(qNaN);
2215 }
else if (name == u"max"_s && argc >= 2) {
2216 expression = maxExpression(argc);
2217 }
else if (name == u"min"_s && argc >= 2) {
2218 expression = minExpression(argc);
2219 }
else if (name == u"pow"_s) {
2220 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
2221 }
else if (name == u"random"_s && argc == 0) {
2222 expression = u"QRandomGenerator::global()->generateDouble()"_s;
2223 }
else if (name == u"round"_s && argc == 1) {
2224 expression = u"std::isfinite(arg1) "
2225 "? ((arg1 < 0.5 && arg1 >= -0.5) "
2226 "? std::copysign(0.0, arg1) "
2227 ": std::floor(arg1 + 0.5)) "
2229 }
else if (name == u"sign"_s && argc == 1) {
2230 expression = u"std::isnan(arg1) "
2234 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(qNaN);
2235 }
else if (name == u"sin"_s && argc == 1) {
2236 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
2237 }
else if (name == u"sinh"_s && argc == 1) {
2238 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
2239 }
else if (name == u"sqrt"_s && argc == 1) {
2240 expression = u"std::sqrt(arg1)"_s;
2241 }
else if (name == u"tan"_s && argc == 1) {
2242 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
2243 }
else if (name == u"tanh"_s && argc == 1) {
2244 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
2245 }
else if (name == u"trunc"_s && argc == 1) {
2246 expression = u"std::trunc(arg1)"_s;
2251 m_body += conversion(m_typeResolver->realType(), m_state.accumulatorOut(), expression);
2260 if (method == u"log" || method == u"debug")
2261 return u"QtDebugMsg"_s;
2262 if (method == u"info")
2263 return u"QtInfoMsg"_s;
2264 if (method == u"warn")
2265 return u"QtWarningMsg"_s;
2266 if (method == u"error")
2267 return u"QtCriticalMsg"_s;
2271bool QQmlJSCodeGenerator::inlineConsoleMethod(
const QString &name,
int argc,
int argv)
2273 const QString type = messageTypeForMethod(name);
2277 addInclude(u"QtCore/qloggingcategory.h"_s);
2280 m_body += u" bool firstArgIsCategory = false;\n";
2281 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(argv) : QQmlJSRegisterContent();
2286 const bool firstArgIsReference = argc > 0
2287 && firstArg.containedType()->isReferenceType();
2289 if (firstArgIsReference) {
2290 m_body += u" QObject *firstArg = ";
2291 m_body += convertStored(
2292 firstArg.storedType(),
2293 m_typeResolver->genericType(firstArg.storedType()),
2294 registerVariable(argv));
2298 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
2299 m_body += firstArgIsReference ? u"firstArg"_sv : u"nullptr"_sv;
2300 m_body += u", &firstArgIsCategory);\n";
2301 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
2303 m_body += u" const QString message = ";
2305 const auto stringConversion = [&](
int i) -> QString {
2306 const QQmlJSScope::ConstPtr read = m_state.readRegister(argv + i).storedType();
2307 const QQmlJSScope::ConstPtr actual = registerType(argv + i).storedType();
2308 if (read == m_typeResolver->stringType()) {
2309 return convertStored(actual, read, consumedRegisterVariable(argv + i));
2310 }
else if (actual->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2311 addInclude(u"QtQml/qjslist.h"_s);
2312 return u"(u'[' + QJSList(&"_s + registerVariable(argv + i)
2313 + u", aotContext->engine).toString() + u']')"_s;
2315 REJECT<QString>(u"converting arguments for console method to string"_s);
2320 if (firstArgIsReference) {
2321 const QString firstArgStringConversion = convertStored(
2322 registerType(argv).storedType(),
2323 m_typeResolver->stringType(), registerVariable(argv));
2324 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
2326 m_body += u".append(QLatin1Char(' ')))).append(";
2330 m_body += stringConversion(0);
2332 m_body += u".append(QLatin1Char(' ')).append(";
2335 for (
int i = 1; i < argc; ++i) {
2337 m_body += u".append(QLatin1Char(' ')).append("_s;
2338 m_body += stringConversion(i) + u')';
2341 m_body += u"QString()";
2344 generateSetInstructionPointer();
2345 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
2351bool QQmlJSCodeGenerator::inlineArrayMethod(
const QString &name,
int base,
int argc,
int argv)
2353 const auto intType = m_typeResolver->int32Type();
2354 const auto elementType = registerType(base).storedType()->elementType();
2355 const auto boolType = m_typeResolver->boolType();
2356 const auto stringType = m_typeResolver->stringType();
2357 const auto baseType = registerType(base);
2359 const QString baseVar = registerVariable(base);
2360 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
2363 addInclude(u"QtQml/qjslist.h"_s);
2365 if (name == u"includes" && argc > 0 && argc < 3) {
2366 QString call = qjsListMethod
2367 + convertStored(registerType(argv).storedType(), elementType,
2368 consumedRegisterVariable(argv));
2370 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2371 consumedRegisterVariable(argv + 1));
2375 m_body += m_state.accumulatorVariableOut + u" = "_s
2376 + conversion(boolType, m_state.accumulatorOut(), call) + u";\n"_s;
2380 if (name == u"toString" || (name == u"join" && argc < 2)) {
2381 QString call = qjsListMethod;
2383 call += convertStored(registerType(argv).storedType(), stringType,
2384 consumedRegisterVariable(argv));
2388 m_body += m_state.accumulatorVariableOut + u" = "_s
2389 + conversion(stringType, m_state.accumulatorOut(), call) + u";\n"_s;
2393 if (name == u"slice" && argc < 3) {
2394 QString call = qjsListMethod;
2395 for (
int i = 0; i < argc; ++i) {
2398 call += convertStored(registerType(argv + i).storedType(), intType,
2399 consumedRegisterVariable(argv + i));
2403 m_body += m_state.accumulatorVariableOut + u" = "_s;
2404 if (baseType.storedType()->isListProperty())
2405 m_body += conversion(m_typeResolver->qObjectListType(), m_state.accumulatorOut(), call);
2407 m_body += conversion(baseType, m_state.accumulatorOut(), call);
2413 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2414 QString call = qjsListMethod
2415 + convertStored(registerType(argv).storedType(), elementType,
2416 consumedRegisterVariable(argv));
2418 call += u", " + convertStored(registerType(argv + 1).storedType(), intType,
2419 consumedRegisterVariable(argv + 1));
2423 m_body += m_state.accumulatorVariableOut + u" = "_s
2424 + conversion(intType, m_state.accumulatorOut(), call) + u";\n"_s;
2431void QQmlJSCodeGenerator::generate_CallPropertyLookup(
int index,
int base,
int argc,
int argv)
2435 const QQmlJSRegisterContent scopeContent = m_state.accumulatorOut().scope();
2436 const QQmlJSScope::ConstPtr scope = scopeContent.containedType();
2438 AccumulatorConverter registers(
this);
2440 const QQmlJSRegisterContent baseType = registerType(base);
2441 const QString name = m_jsUnitGenerator->lookupName(index);
2443 if (scope == m_typeResolver->mathObject()) {
2444 if (inlineMathMethod(name, argc, argv))
2446 }
else if (scope == m_typeResolver->consoleObject()) {
2447 if (inlineConsoleMethod(name, argc, argv))
2449 }
else if (scope == m_typeResolver->stringType()) {
2450 if (inlineStringMethod(name, base, argc, argv))
2452 }
else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2453 if (inlineArrayMethod(name, base, argc, argv))
2457 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2458 REJECT(u"call to untyped JavaScript function"_s);
2463 if (scope->isReferenceType()) {
2464 const QString inputPointer = resolveQObjectPointer(
2465 scope, baseType, registerVariable(base),
2466 u"Cannot call method '%1' of %2"_s.arg(name));
2468 const QString initMethodTemplate = m_state.isShadowable()
2469 ? u"initCallObjectPropertyLookupAsVariant(%1, %2)"_s
2470 : u"initCallObjectPropertyLookup(%1, %2, %3)"_s;
2472 m_body += initAndCall(
2474 u"callObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2475 initMethodTemplate.arg(index).arg(inputPointer), &outVar);
2477 const QQmlJSScope::ConstPtr originalScope
2478 = m_typeResolver->original(scopeContent).containedType();
2479 const QString inputPointer = resolveValueTypeContentPointer(
2480 originalScope, baseType, registerVariable(base),
2481 u"Cannot call method '%1' of %2"_s.arg(name));
2483 m_body += initAndCall(
2485 u"callValueLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
2486 u"initCallValueLookup(%1, %2, %3)"_s
2487 .arg(index).arg(metaObject(originalScope)),
2491 const QString lookup = u"doCall()"_s;
2492 const QString initialization = u"doInit()"_s;
2493 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2494 generateLookup(lookup, initialization, preparation);
2495 generateMoveOutVarAfterCall(outVar);
2499 if (scope->isReferenceType())
2502 const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
2503 if (!method.isConst())
2504 generateWriteBack(base);
2507void QQmlJSCodeGenerator::generate_CallName(
int name,
int argc,
int argv)
2515void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(
int argc,
int argv)
2522void QQmlJSCodeGenerator::generate_CallGlobalLookup(
int index,
int argc,
int argv)
2527 REJECT(u"CallGlobalLookup"_s);
2530void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(
int index,
int argc,
int argv)
2534 if (m_state.accumulatorOut().scope().contains(m_typeResolver->jsGlobalObject())) {
2535 const QString name = m_jsUnitGenerator->stringForIndex(
2536 m_jsUnitGenerator->lookupNameIndex(index));
2537 if (inlineTranslateMethod(name, argc, argv))
2541 if (m_state.accumulatorOut().isJavaScriptReturnValue())
2542 REJECT(u"call to untyped JavaScript function"_s);
2544 AccumulatorConverter registers(
this);
2548 m_body += initAndCall(
2549 argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
2550 u"initCallQmlContextPropertyLookup(%1, %2)"_s.arg(index), &outVar);
2552 const QString lookup = u"doCall()"_s;
2553 const QString initialization = u"doInit()"_s;
2554 const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
2555 generateLookup(lookup, initialization, preparation);
2556 generateMoveOutVarAfterCall(outVar);
2561void QQmlJSCodeGenerator::generate_CallWithSpread(
int func,
int thisObject,
int argc,
int argv)
2564 Q_UNUSED(thisObject)
2570void QQmlJSCodeGenerator::generate_TailCall(
int func,
int thisObject,
int argc,
int argv)
2573 Q_UNUSED(thisObject)
2579void QQmlJSCodeGenerator::generate_Construct(
int func,
int argc,
int argv)
2584 const auto originalResult = originalType(m_state.accumulatorOut());
2586 if (originalResult.contains(m_typeResolver->dateTimeType())) {
2587 m_body += m_state.accumulatorVariableOut + u" = ";
2589 m_body += conversion(
2590 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2591 u"QDateTime::currentDateTime()"_s) + u";\n";
2595 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->dateTimeType())) {
2596 m_body += conversion(
2597 registerType(argv), m_state.readRegister(argv), registerVariable(argv))
2603 constexpr int maxArgc = 7;
2604 for (
int i = 0; i < std::min(argc, maxArgc); ++i) {
2607 ctorArgs += conversion(
2608 registerType(argv + i), m_state.readRegister(argv + i),
2609 registerVariable(argv + i));
2611 m_body += conversion(
2612 m_typeResolver->dateTimeType(), m_state.accumulatorOut(),
2613 u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
2617 if (originalResult.contains(m_typeResolver->variantListType())) {
2620 if (argc == 1 && m_state.readRegister(argv).contains(m_typeResolver->realType())) {
2621 addInclude(u"QtQml/qjslist.h"_s);
2623 const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
2624 + u"QLatin1String(\"Invalid array length\"));\n"_s;
2626 const QString indexName = registerVariable(argv);
2627 const auto indexType = registerType(argv).containedType();
2628 if (!m_typeResolver->isNativeArrayIndex(indexType)) {
2629 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
2631 generateReturnError();
2633 }
else if (!m_typeResolver->isUnsignedInteger(indexType)) {
2634 m_body += u"if ("_s + indexName + u" < 0) {\n"_s
2636 generateReturnError();
2640 m_body += m_state.accumulatorVariableOut + u" = "_s
2641 + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
2642 m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
2643 + u", aotContext->engine).resize("_s
2645 registerType(argv).storedType(), m_typeResolver->sizeType(),
2646 consumedRegisterVariable(argv))
2648 }
else if (!m_logger->currentFunctionHasCompileError()) {
2649 generateArrayInitializer(argc, argv);
2654 const QQmlJSScope::ConstPtr originalContained = originalResult.containedType();
2655 if (originalContained->isValueType() && originalResult.isMethodCall()) {
2656 const QQmlJSMetaMethod ctor = originalResult.methodCall();
2657 if (ctor.isJavaScriptFunction())
2658 REJECT(u"calling JavaScript constructor "_s + ctor.methodName());
2660 QList<QQmlJSRegisterContent> argumentTypes;
2661 QStringList arguments;
2662 for (
int i = 0; i < argc; ++i) {
2663 argumentTypes.append(registerType(argv + i));
2664 arguments.append(consumedRegisterVariable(argv + i));
2667 const QQmlJSScope::ConstPtr extension = originalContained->extensionType().scope;
2668 const QString result = generateCallConstructor(
2669 ctor, argumentTypes, arguments, metaType(originalContained),
2670 metaObject(extension ? extension : originalContained));
2672 m_body += m_state.accumulatorVariableOut + u" = "_s
2673 + conversion(m_pool->storedIn(originalResult, m_typeResolver->varType()),
2674 m_state.accumulatorOut(), result)
2684void QQmlJSCodeGenerator::generate_ConstructWithSpread(
int func,
int argc,
int argv)
2692void QQmlJSCodeGenerator::generate_SetUnwindHandler(
int offset)
2695 REJECT(u"SetUnwindHandler"_s);
2698void QQmlJSCodeGenerator::generate_UnwindDispatch()
2700 REJECT(u"UnwindDispatch"_s);
2703void QQmlJSCodeGenerator::generate_UnwindToLabel(
int level,
int offset)
2710void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(
int name)
2718void QQmlJSCodeGenerator::generate_ThrowException()
2722 generateSetInstructionPointer();
2723 m_body += u"aotContext->engine->throwError("_s + conversion(
2724 m_state.accumulatorIn(),
2725 m_typeResolver->jsValueType(),
2726 m_state.accumulatorVariableIn) + u");\n"_s;
2727 generateReturnError();
2728 m_skipUntilNextLabel =
true;
2732void QQmlJSCodeGenerator::generate_GetException()
2737void QQmlJSCodeGenerator::generate_SetException()
2742void QQmlJSCodeGenerator::generate_CreateCallContext()
2749void QQmlJSCodeGenerator::generate_PushCatchContext(
int index,
int nameIndex)
2753 REJECT(u"PushCatchContext"_s);
2756void QQmlJSCodeGenerator::generate_PushWithContext()
2761void QQmlJSCodeGenerator::generate_PushBlockContext(
int index)
2767void QQmlJSCodeGenerator::generate_CloneBlockContext()
2772void QQmlJSCodeGenerator::generate_PushScriptContext(
int index)
2778void QQmlJSCodeGenerator::generate_PopScriptContext()
2783void QQmlJSCodeGenerator::generate_PopContext()
2788 m_body += u"{}\n}\n"_s;
2791void QQmlJSCodeGenerator::generate_GetIterator(
int iterator)
2795 addInclude(u"QtQml/qjslist.h"_s);
2796 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2797 if (!listType.isList())
2798 REJECT(u"iterator on non-list type"_s);
2800 const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
2801 if (!iteratorType.isProperty())
2802 REJECT(u"using non-iterator as iterator"_s);
2804 const QString identifier = QString::number(iteratorType.baseLookupIndex());
2805 QString baseName = m_state.accumulatorVariableOut.mid(2);
2806 const QString iteratorName = baseName + u"Iterator" + identifier;
2807 const QString listName = baseName + u"List" + identifier;
2809 m_body += u"QJSListFor"_s
2810 + (iterator ==
int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
2811 + u"Iterator "_s + iteratorName + u";\n";
2812 m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
2814 m_body += m_state.accumulatorVariableOut + u"->init(";
2815 if (iterator ==
int(QQmlJS::AST::ForEachType::In)) {
2816 if (!iteratorType.isStoredIn(m_typeResolver->forInIteratorPtr()))
2817 REJECT(u"using non-iterator as iterator"_s);
2818 m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
2822 if (iterator ==
int(QQmlJS::AST::ForEachType::Of)) {
2823 if (!iteratorType.isStoredIn(m_typeResolver->forOfIteratorPtr()))
2824 REJECT(u"using non-iterator as iterator"_s);
2825 m_body += u"const auto &"
2826 + listName + u" = " + consumedAccumulatorVariableIn();
2830void QQmlJSCodeGenerator::generate_IteratorNext(
int value,
int offset)
2834 Q_ASSERT(value == m_state.changedRegisterIndex());
2835 const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
2836 if (!iteratorContent.isProperty())
2837 REJECT(u"using non-iterator as iterator"_s);
2839 const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
2840 const QString listName = m_state.accumulatorVariableIn
2841 + u"List" + QString::number(iteratorContent.baseLookupIndex());
2843 if (iteratorType == m_typeResolver->forOfIteratorPtr())
2844 qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
2845 else if (iteratorType != m_typeResolver->forInIteratorPtr())
2846 REJECT(u"using non-iterator as iterator"_s);
2848 m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
2851 QQmlJSRegisterContent iteratorValue = m_typeResolver->extractNonVoidFromOptionalType(
2852 m_typeResolver->original(m_state.changedRegister()));
2853 iteratorValue = m_pool->storedIn(iteratorValue, iteratorValue.containedType());
2855 m_body += changedRegisterVariable() + u" = "
2857 iteratorValue, m_state.changedRegister(),
2858 m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
2860 m_body += u"} else {\n ";
2861 m_body += changedRegisterVariable() + u" = "
2862 + conversion(m_typeResolver->voidType(), m_state.changedRegister(), QString());
2864 generateJumpCodeWithTypeConversions(offset);
2868void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(
int iterator,
int object,
int offset)
2876void QQmlJSCodeGenerator::generate_IteratorClose()
2881void QQmlJSCodeGenerator::generate_DestructureRestElement()
2886void QQmlJSCodeGenerator::generate_DeleteProperty(
int base,
int index)
2893void QQmlJSCodeGenerator::generate_DeleteName(
int name)
2899void QQmlJSCodeGenerator::generate_TypeofName(
int name)
2905void QQmlJSCodeGenerator::generate_TypeofValue()
2907 REJECT(u"TypeofValue"_s);
2910void QQmlJSCodeGenerator::generate_DeclareVar(
int varName,
int isDeletable)
2913 Q_UNUSED(isDeletable)
2917void QQmlJSCodeGenerator::generate_DefineArray(
int argc,
int args)
2922 if (!m_logger->currentFunctionHasCompileError())
2923 generateArrayInitializer(argc, args);
2926void QQmlJSCodeGenerator::generate_DefineObjectLiteral(
int internalClassId,
int argc,
int args)
2930 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
2931 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value)
2932 REJECT(u"storing an object literal in a non-value type"_s);
2934 const QQmlJSScope::ConstPtr contained = m_state.accumulatorOut().containedType();
2936 const int classSize = m_jsUnitGenerator->jsClassSize(internalClassId);
2943 if (argc > classSize)
2944 REJECT(u"non-literal keys of object literals"_s);
2946 Q_ASSERT(argc == classSize);
2948 const auto createVariantMap = [&]() {
2950 result += u"QVariantMap {\n";
2951 const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
2952 for (
int i = 0; i < classSize; ++i) {
2954 + QQmlJSUtils::toLiteral(m_jsUnitGenerator->jsClassMember(internalClassId, i))
2956 const int currentArg = args + i;
2957 const QQmlJSScope::ConstPtr argType = registerType(currentArg).storedType();
2958 const QString consumedArg = consumedRegisterVariable(currentArg);
2959 result += convertStored(argType, propType, consumedArg) + u" },\n";
2967 if (contained == m_typeResolver->varType() || contained == m_typeResolver->variantMapType()) {
2968 m_body += m_state.accumulatorVariableOut + u" = "_s + createVariantMap() + u";\n"_s;
2972 if (contained == m_typeResolver->jsValueType()) {
2973 m_body += m_state.accumulatorVariableOut + u" = aotContext->engine->toScriptValue("_s
2974 + createVariantMap() + u");\n"_s;
2978 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
2979 const bool isVariantOrPrimitive = (stored == m_typeResolver->varType())
2980 || (stored == m_typeResolver->jsPrimitiveType());
2982 if (m_state.accumulatorOut().contains(stored)) {
2984 }
else if (isVariantOrPrimitive) {
2985 m_body += u'(' + metaType(m_state.accumulatorOut().containedType()) + u')';
2987 REJECT(u"storing an object literal in an unsupported container %1"_s
2988 .arg(stored->internalName()));
2995 bool isExtension =
false;
2996 if (!m_typeResolver->canPopulate(contained, m_typeResolver->variantMapType(), &isExtension))
2997 REJECT(u"storing an object literal in a non-structured value type"_s);
2999 const QQmlJSScope::ConstPtr accessor = isExtension
3000 ? contained->extensionType().scope
3004 m_body += u" const QMetaObject *meta = ";
3005 if (!isExtension && isVariantOrPrimitive)
3006 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
3008 m_body += metaObject(accessor);
3011 for (
int i = 0; i < classSize; ++i) {
3013 const QString propName = m_jsUnitGenerator->jsClassMember(internalClassId, i);
3014 const int currentArg = args + i;
3015 const QQmlJSRegisterContent propType = m_state.readRegister(currentArg);
3016 const QQmlJSRegisterContent argType = registerType(currentArg);
3017 const QQmlJSMetaProperty property = contained->property(propName);
3018 const QString consumedArg = consumedRegisterVariable(currentArg);
3019 QString argument = conversion(argType, propType, consumedArg);
3021 if (argument == consumedArg) {
3022 argument = registerVariable(currentArg);
3024 m_body += u" "_s + propType.storedType()->augmentedInternalName()
3025 + u" arg = "_s + argument + u";\n";
3026 argument = u"arg"_s;
3029 int index = property.index();
3033 const QString indexString = QString::number(index);
3034 m_body += u" void *argv[] = { %1, nullptr };\n"_s
3035 .arg(contentPointer(propType, argument));
3036 m_body += u" meta->property("_s + indexString;
3037 m_body += u").enclosingMetaObject()->d.static_metacall(reinterpret_cast<QObject *>(";
3038 m_body += contentPointer(m_state.accumulatorOut(), m_state.accumulatorVariableOut);
3039 m_body += u"), QMetaObject::WriteProperty, " + indexString + u", argv);\n";
3047void QQmlJSCodeGenerator::generate_CreateClass(
int classIndex,
int heritage,
int computedNames)
3049 Q_UNUSED(classIndex)
3051 Q_UNUSED(computedNames)
3055void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
3060void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
3065void QQmlJSCodeGenerator::generate_CreateRestParameter(
int argIndex)
3071void QQmlJSCodeGenerator::generate_ConvertThisToObject()
3075 m_body += changedRegisterVariable() + u" = "_s
3076 + conversion(m_typeResolver->qObjectType(), m_state.changedRegister(),
3077 u"aotContext->thisObject()"_s)
3081void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
3086void QQmlJSCodeGenerator::generate_ToObject()
3091void QQmlJSCodeGenerator::generate_Jump(
int offset)
3095 generateJumpCodeWithTypeConversions(offset);
3096 m_skipUntilNextLabel =
true;
3100void QQmlJSCodeGenerator::generate_JumpTrue(
int offset)
3104 m_body += u"if ("_s;
3105 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3106 m_state.accumulatorVariableIn);
3108 generateJumpCodeWithTypeConversions(offset);
3111void QQmlJSCodeGenerator::generate_JumpFalse(
int offset)
3115 m_body += u"if (!"_s;
3116 m_body += convertStored(m_state.accumulatorIn().storedType(), m_typeResolver->boolType(),
3117 m_state.accumulatorVariableIn);
3119 generateJumpCodeWithTypeConversions(offset);
3122void QQmlJSCodeGenerator::generate_JumpNoException(
int offset)
3126 m_body += u"if (!context->engine->hasException()) "_s;
3127 generateJumpCodeWithTypeConversions(offset);
3130void QQmlJSCodeGenerator::generate_JumpNotUndefined(
int offset)
3136void QQmlJSCodeGenerator::generate_CheckException()
3140 generateExceptionCheck();
3143void QQmlJSCodeGenerator::generate_CmpEqNull()
3146 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
false);
3149void QQmlJSCodeGenerator::generate_CmpNeNull()
3152 generateEqualityOperation(literalType(m_typeResolver->nullType()), QString(), u"equals"_s,
true);
3155QString QQmlJSCodeGenerator::getLookupPreparation(
3156 QQmlJSRegisterContent content,
const QString &var,
int lookup)
3158 if (content.contains(content.storedType()))
3161 if (content.isStoredIn(m_typeResolver->varType())) {
3162 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
3163 + QString::number(lookup) + u"))"_s;
3166 if (content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3167 return var + u" = QJSPrimitiveValue(aotContext->lookupResultMetaType("_s
3168 + QString::number(lookup) + u"))"_s;
3175QString QQmlJSCodeGenerator::contentPointer(QQmlJSRegisterContent content,
const QString &var)
3177 const QQmlJSScope::ConstPtr stored = content.storedType();
3178 if (content.contains(stored))
3181 if (content.isStoredIn(m_typeResolver->varType())
3182 || content.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3183 return var + u".data()"_s;
3186 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3189 if (m_typeResolver->isNumeric(content.storedType())
3190 && content.containedType()->scopeType() == QQmlSA::ScopeType::EnumScope) {
3194 if (stored->isListProperty() && content.containedType()->isListProperty())
3198 u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
3201QString QQmlJSCodeGenerator::contentType(QQmlJSRegisterContent content,
const QString &var)
3203 const QQmlJSScope::ConstPtr stored = content.storedType();
3204 const QQmlJSScope::ConstPtr contained = content.containedType();
3205 if (contained == stored)
3206 return metaTypeFromType(stored);
3208 if (stored == m_typeResolver->varType() || stored == m_typeResolver->jsPrimitiveType())
3209 return var + u".metaType()"_s;
3211 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3212 return metaType(contained);
3214 const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(contained);
3215 if (m_typeResolver->isNumeric(stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
3216 return metaTypeFromType(nonComposite->baseType());
3218 if (stored->isListProperty() && contained->isListProperty())
3219 return metaType(contained);
3222 u"content type of unsupported wrapper type "_s + content.descriptiveName());
3225void QQmlJSCodeGenerator::generate_CmpEqInt(
int lhsConst)
3229 generateEqualityOperation(
3230 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
false);
3233void QQmlJSCodeGenerator::generate_CmpNeInt(
int lhsConst)
3237 generateEqualityOperation(
3238 literalType(m_typeResolver->int32Type()), QString::number(lhsConst), u"equals"_s,
true);
3241void QQmlJSCodeGenerator::generate_CmpEq(
int lhs)
3244 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
false);
3247void QQmlJSCodeGenerator::generate_CmpNe(
int lhs)
3250 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"equals"_s,
true);
3253void QQmlJSCodeGenerator::generate_CmpGt(
int lhs)
3256 generateCompareOperation(lhs, u">"_s);
3259void QQmlJSCodeGenerator::generate_CmpGe(
int lhs)
3262 generateCompareOperation(lhs, u">="_s);
3265void QQmlJSCodeGenerator::generate_CmpLt(
int lhs)
3268 generateCompareOperation(lhs, u"<"_s);
3271void QQmlJSCodeGenerator::generate_CmpLe(
int lhs)
3274 generateCompareOperation(lhs, u"<="_s);
3277void QQmlJSCodeGenerator::generate_CmpStrictEqual(
int lhs)
3280 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
false);
3283void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(
int lhs)
3286 generateEqualityOperation(registerType(lhs), registerVariable(lhs), u"strictlyEquals"_s,
true);
3289void QQmlJSCodeGenerator::generate_CmpIn(
int lhs)
3295void QQmlJSCodeGenerator::generate_CmpInstanceOf(
int lhs)
3301void QQmlJSCodeGenerator::generate_As(
int lhs)
3305 const QString input = registerVariable(lhs);
3306 const QQmlJSRegisterContent inputContent = m_state.readRegister(lhs);
3307 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
3311 const QQmlJSRegisterContent originalContent = originalType(outputContent);
3312 QQmlJSScope::ConstPtr target;
3313 if (originalContent.containedType()->isReferenceType())
3314 target = originalContent.containedType();
3315 else if (originalContent.isConversion())
3316 target = m_typeResolver->extractNonVoidFromOptionalType(originalContent).containedType();
3317 else if (originalContent.variant() == QQmlJSRegisterContent::Cast)
3318 target = originalContent.containedType();
3321 REJECT(u"type assertion to unknown type"_s);
3323 const bool isTrivial = m_typeResolver->inherits(
3324 m_typeResolver->originalContainedType(inputContent), target);
3326 m_body += m_state.accumulatorVariableOut + u" = "_s;
3328 if (!isTrivial && target->isReferenceType()) {
3329 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(target);
3330 const QString inputConversion = inputContent.storedType()->isReferenceType()
3332 : convertStored(inputContent.storedType(), genericContained, input);
3334 if (target->isComposite()
3335 && m_state.accumulatorIn().isStoredIn(m_typeResolver->metaObjectType())) {
3336 m_body += conversion(
3337 genericContained, outputContent,
3338 m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
3340 m_body += conversion(
3341 genericContained, outputContent,
3342 u'(' + metaObject(target) + u")->cast("_s + inputConversion + u')');
3348 if (inputContent.isStoredIn(m_typeResolver->varType())
3349 || inputContent.isStoredIn(m_typeResolver->jsPrimitiveType())) {
3351 const auto source = m_typeResolver->extractNonVoidFromOptionalType(
3352 originalType(inputContent)).containedType();
3354 if (source && source == target) {
3355 m_body += input + u".metaType() == "_s + metaType(target)
3356 + u" ? " + conversion(inputContent, outputContent, input)
3357 + u" : " + conversion(
3358 literalType(m_typeResolver->voidType()), outputContent, QString());
3366 m_body += conversion(inputContent, m_state.accumulatorOut(), input) + u";\n"_s;
3370 REJECT(u"non-trivial value type assertion"_s);
3373void QQmlJSCodeGenerator::generate_UNot()
3376 generateUnaryOperation(u"!"_s);
3379void QQmlJSCodeGenerator::generate_UPlus()
3382 generateUnaryOperation(u"+"_s);
3385void QQmlJSCodeGenerator::generate_UMinus()
3388 generateUnaryOperation(u"-"_s);
3391void QQmlJSCodeGenerator::generate_UCompl()
3394 generateUnaryOperation(u"~"_s);
3397void QQmlJSCodeGenerator::generate_Increment()
3400 generateInPlaceOperation(u"++"_s);
3403void QQmlJSCodeGenerator::generate_Decrement()
3406 generateInPlaceOperation(u"--"_s);
3409void QQmlJSCodeGenerator::generate_Add(
int lhs)
3412 generateArithmeticOperation(lhs, u"+"_s);
3415void QQmlJSCodeGenerator::generate_BitAnd(
int lhs)
3418 generateArithmeticOperation(lhs, u"&"_s);
3421void QQmlJSCodeGenerator::generate_BitOr(
int lhs)
3424 generateArithmeticOperation(lhs, u"|"_s);
3427void QQmlJSCodeGenerator::generate_BitXor(
int lhs)
3430 generateArithmeticOperation(lhs, u"^"_s);
3433void QQmlJSCodeGenerator::generate_UShr(
int lhs)
3436 generateShiftOperation(lhs, u">>"_s);
3439void QQmlJSCodeGenerator::generate_Shr(
int lhs)
3442 generateShiftOperation(lhs, u">>"_s);
3445void QQmlJSCodeGenerator::generate_Shl(
int lhs)
3448 generateShiftOperation(lhs, u"<<"_s);
3451void QQmlJSCodeGenerator::generate_BitAndConst(
int rhs)
3454 generateArithmeticConstOperation(rhs, u"&"_s);
3457void QQmlJSCodeGenerator::generate_BitOrConst(
int rhs)
3460 generateArithmeticConstOperation(rhs, u"|"_s);
3463void QQmlJSCodeGenerator::generate_BitXorConst(
int rhs)
3466 generateArithmeticConstOperation(rhs, u"^"_s);
3469void QQmlJSCodeGenerator::generate_UShrConst(
int rhs)
3472 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3475void QQmlJSCodeGenerator::generate_ShrConst(
int rhs)
3478 generateArithmeticConstOperation(rhs & 0x1f, u">>"_s);
3481void QQmlJSCodeGenerator::generate_ShlConst(
int rhs)
3484 generateArithmeticConstOperation(rhs & 0x1f, u"<<"_s);
3487void QQmlJSCodeGenerator::generate_Exp(
int lhs)
3491 const QString lhsString = conversion(
3492 registerType(lhs), m_state.readRegister(lhs), consumedRegisterVariable(lhs));
3493 const QString rhsString = conversion(
3494 m_state.accumulatorIn(), m_state.readAccumulator(),
3495 consumedAccumulatorVariableIn());
3497 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsString.isEmpty());
3498 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsString.isEmpty());
3500 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3501 m_body += m_state.accumulatorVariableOut + u" = "_s;
3502 m_body += conversion(
3503 originalOut, m_state.accumulatorOut(),
3504 u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
3508void QQmlJSCodeGenerator::generate_Mul(
int lhs)
3511 generateArithmeticOperation(lhs, u"*"_s);
3514void QQmlJSCodeGenerator::generate_Div(
int lhs)
3517 generateArithmeticOperation(lhs, u"/"_s);
3520void QQmlJSCodeGenerator::generate_Mod(
int lhs)
3524 const auto lhsVar = convertStored(
3525 registerType(lhs).storedType(), m_typeResolver->jsPrimitiveType(),
3526 consumedRegisterVariable(lhs));
3527 const auto rhsVar = convertStored(
3528 m_state.accumulatorIn().storedType(), m_typeResolver->jsPrimitiveType(),
3529 consumedAccumulatorVariableIn());
3530 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhsVar.isEmpty());
3531 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhsVar.isEmpty());
3533 m_body += m_state.accumulatorVariableOut;
3535 m_body += conversion(m_typeResolver->jsPrimitiveType(), m_state.accumulatorOut(),
3536 u'(' + lhsVar + u" % "_s + rhsVar + u')');
3540void QQmlJSCodeGenerator::generate_Sub(
int lhs)
3543 generateArithmeticOperation(lhs, u"-"_s);
3546void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(
int firstReg,
int count)
3553void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
3558void QQmlJSCodeGenerator::generate_GetTemplateObject(
int index)
3564QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
3565 QV4::Moth::Instr::Type type)
3567 m_state.State::operator=(nextStateFromAnnotations(m_state, m_annotations));
3568 const auto accumulatorIn = m_state.registers.find(Accumulator);
3569 if (accumulatorIn != m_state.registers.end()
3570 && isTypeStorable(m_typeResolver, accumulatorIn.value().content.storedType())) {
3571 QQmlJSRegisterContent content = accumulatorIn.value().content;
3572 m_state.accumulatorVariableIn = m_registerVariables.value(content).variableName;
3573 Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
3575 m_state.accumulatorVariableIn.clear();
3578 auto labelIt = m_labels.constFind(currentInstructionOffset());
3579 if (labelIt != m_labels.constEnd()) {
3580 m_body += *labelIt + u":;\n"_s;
3581 m_skipUntilNextLabel =
false;
3582 }
else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
3583 return SkipInstruction;
3586 if (m_state.changedRegisterIndex() == Accumulator)
3587 m_state.accumulatorVariableOut = changedRegisterVariable();
3589 m_state.accumulatorVariableOut.clear();
3593 Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
3594 || m_state.accumulatorVariableOut.isEmpty());
3595 Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
3596 || !m_state.accumulatorVariableOut.isEmpty()
3597 || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
3601 if (!m_state.hasInternalSideEffects() && changedRegisterVariable().isEmpty()) {
3602 generateJumpCodeWithTypeConversions(0);
3603 return SkipInstruction;
3606 return ProcessInstruction;
3609void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
3611 if (!m_skipUntilNextLabel)
3612 generateJumpCodeWithTypeConversions(0);
3613 m_pool->clearTemporaries();
3616void QQmlJSCodeGenerator::generateSetInstructionPointer()
3618 m_body += u"aotContext->setInstructionPointer("_s
3619 + QString::number(nextInstructionOffset()) + u");\n"_s;
3622void QQmlJSCodeGenerator::generateExceptionCheck()
3624 m_body += u"if (aotContext->engine->hasError()) {\n"_s;
3625 generateReturnError();
3629void QQmlJSCodeGenerator::generateEqualityOperation(
3630 QQmlJSRegisterContent lhsContent, QQmlJSRegisterContent rhsContent,
3631 const QString &lhsName,
const QString &rhsName,
const QString &function,
bool invert)
3633 const bool lhsIsOptional = m_typeResolver->isOptionalType(lhsContent);
3634 const bool rhsIsOptional = m_typeResolver->isOptionalType(rhsContent);
3636 const QQmlJSScope::ConstPtr rhsContained = rhsIsOptional
3637 ? m_typeResolver->extractNonVoidFromOptionalType(rhsContent).containedType()
3638 : rhsContent.containedType();
3640 const QQmlJSScope::ConstPtr lhsContained = lhsIsOptional
3641 ? m_typeResolver->extractNonVoidFromOptionalType(lhsContent).containedType()
3642 : lhsContent.containedType();
3644 const bool isStrict = function ==
"strictlyEquals"_L1;
3645 const bool strictlyComparableWithVar
3646 = isStrict && canStrictlyCompareWithVar(m_typeResolver, lhsContained, rhsContained);
3647 auto isComparable = [&]() {
3648 if (m_typeResolver->isPrimitive(lhsContent) && m_typeResolver->isPrimitive(rhsContent))
3650 if (m_typeResolver->isNumeric(lhsContent) && m_typeResolver->isNumeric(rhsContent))
3652 if (m_typeResolver->isNumeric(lhsContent) && rhsContent.isEnumeration())
3654 if (m_typeResolver->isNumeric(rhsContent) && lhsContent.isEnumeration())
3656 if (strictlyComparableWithVar)
3658 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained))
3660 if (canCompareWithQUrl(m_typeResolver, lhsContained, rhsContained))
3665 const auto retrieveOriginal = [
this](QQmlJSRegisterContent content) {
3666 const auto contained = content.containedType();
3667 const auto originalContent = originalType(content);
3668 const auto containedOriginal = originalContent.containedType();
3670 if (originalContent.isStoredIn(m_typeResolver->genericType(containedOriginal))) {
3672 return originalContent;
3673 }
else if (contained == containedOriginal) {
3674 if (originalContent.isConversion()) {
3676 return m_pool->storedIn(originalContent, content.storedType());
3678 }
else if (m_typeResolver->canHold(contained, containedOriginal)) {
3679 return m_pool->storedIn(originalContent, content.storedType());
3685 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
3686 const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
3688 if (!isComparable()) {
3689 QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
3690 QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
3691 if (lhsOriginal.containedType() != lhsContent.containedType()
3692 || lhsOriginal.storedType() != lhsType
3693 || rhsOriginal.containedType() != rhsContent.containedType()
3694 || rhsOriginal.storedType() != rhsType) {
3697 generateEqualityOperation(lhsOriginal, rhsOriginal,
3698 conversion(lhsType, lhsOriginal, lhsName),
3699 conversion(rhsType, rhsOriginal, rhsName),
3704 REJECT(u"incomparable types %1 and %2"_s.arg(
3705 rhsContent.descriptiveName(), lhsContent.descriptiveName()));
3708 if (strictlyComparableWithVar) {
3710 if (!lhsName.isEmpty() && rhsName.isEmpty()) {
3712 generateVariantEqualityComparison(rhsContent, lhsName, invert);
3716 if (!rhsName.isEmpty() && lhsName.isEmpty()) {
3718 generateVariantEqualityComparison(lhsContent, rhsName, invert);
3722 if (lhsContent.contains(m_typeResolver->varType())) {
3723 generateVariantEqualityComparison(rhsContent, rhsName, lhsName, invert);
3727 if (rhsContent.contains(m_typeResolver->varType())) {
3728 generateVariantEqualityComparison(lhsContent, lhsName, rhsName, invert);
3734 REJECT(u"comparison of optional null"_s);
3737 const auto comparison = [&]() -> QString {
3738 const auto primitive = m_typeResolver->jsPrimitiveType();
3739 const QString sign = invert ? u" != "_s : u" == "_s;
3741 if (lhsType == rhsType && lhsType != primitive && lhsType != m_typeResolver->varType()) {
3746 if (isTypeStorable(m_typeResolver, lhsType))
3747 return lhsName + sign + rhsName;
3750 return invert ? u"false"_s : u"true"_s;
3753 if (canCompareWithQObject(m_typeResolver, lhsType, rhsType)) {
3755 return (isTypeStorable(m_typeResolver, lhsType) ? lhsName : u"nullptr"_s)
3757 + (isTypeStorable(m_typeResolver, rhsType) ? rhsName : u"nullptr"_s);
3760 if (canCompareWithQObject(m_typeResolver, lhsContained, rhsContained)) {
3767 if (lhsIsOptional) {
3768 if (rhsIsOptional) {
3771 + lhsName + u".isValid() && !"_s
3772 + rhsName + u".isValid()) || "_s;
3775 result += u'(' + lhsName + u".isValid() && "_s;
3780 if (rhsIsOptional) {
3781 result += rhsName + u".isValid() && "_s;
3788 Q_ASSERT(lhsType != m_typeResolver->voidType());
3789 Q_ASSERT(rhsType != m_typeResolver->voidType());
3791 const auto resolvedName = [&](
const QString name) -> QString {
3793 const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
3796 : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
3799 const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
3800 const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
3802 return (invert ? u"!("_s : u"("_s) + result
3803 + (isTypeStorable(m_typeResolver, lhsType) ? lhsResolved : u"nullptr"_s)
3805 + (isTypeStorable(m_typeResolver, rhsType) ? rhsResolved : u"nullptr"_s)
3809 if ((m_typeResolver->isUnsignedInteger(rhsType)
3810 && m_typeResolver->isUnsignedInteger(lhsType))
3811 || (m_typeResolver->isSignedInteger(rhsType)
3812 && m_typeResolver->isSignedInteger(lhsType))) {
3814 return lhsName + sign + rhsName;
3817 if (rhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(lhsType)) {
3819 return lhsName + sign + convertStored(rhsType, lhsType, rhsName);
3822 if (lhsType == m_typeResolver->boolType() && m_typeResolver->isIntegral(rhsType)) {
3824 return convertStored(lhsType, rhsType, lhsName) + sign + rhsName;
3827 if (m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(rhsType)) {
3829 return convertStored(lhsType, m_typeResolver->realType(), lhsName)
3831 + convertStored(rhsType, m_typeResolver->realType(), rhsName);
3835 return (invert ? u"!"_s : QString())
3836 + convertStored(lhsType, primitive, lhsName)
3837 + u'.' + function + u'(' + convertStored(rhsType, primitive, rhsName) + u')';
3840 m_body += m_state.accumulatorVariableOut + u" = "_s;
3841 m_body += conversion(m_typeResolver->boolType(), m_state.accumulatorOut(), comparison());
3845void QQmlJSCodeGenerator::generateCompareOperation(
int lhs,
const QString &cppOperator)
3847 m_body += m_state.accumulatorVariableOut + u" = "_s;
3849 const auto lhsType = registerType(lhs);
3850 const QQmlJSScope::ConstPtr compareType =
3851 m_typeResolver->isNumeric(lhsType) && m_typeResolver->isNumeric(m_state.accumulatorIn())
3852 ? m_typeResolver->merge(lhsType.storedType(), m_state.accumulatorIn().storedType())
3853 : m_typeResolver->jsPrimitiveType();
3855 m_body += conversion(
3856 m_typeResolver->boolType(), m_state.accumulatorOut(),
3857 convertStored(registerType(lhs).storedType(), compareType,
3858 consumedRegisterVariable(lhs))
3859 + u' ' + cppOperator + u' '
3860 + convertStored(m_state.accumulatorIn().storedType(), compareType,
3861 consumedAccumulatorVariableIn()));
3865void QQmlJSCodeGenerator::generateArithmeticOperation(
int lhs,
const QString &cppOperator)
3867 generateArithmeticOperation(
3868 conversion(registerType(lhs), m_state.readRegister(lhs),
3869 consumedRegisterVariable(lhs)),
3870 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3871 consumedAccumulatorVariableIn()),
3875void QQmlJSCodeGenerator::generateShiftOperation(
int lhs,
const QString &cppOperator)
3877 generateArithmeticOperation(
3878 conversion(registerType(lhs), m_state.readRegister(lhs),
3879 consumedRegisterVariable(lhs)),
3880 u'(' + conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3881 consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
3885void QQmlJSCodeGenerator::generateArithmeticOperation(
3886 const QString &lhs,
const QString &rhs,
const QString &cppOperator)
3888 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !lhs.isEmpty());
3889 Q_ASSERT(m_logger->currentFunctionHasCompileError() || !rhs.isEmpty());
3891 const QQmlJSRegisterContent originalOut = originalType(m_state.accumulatorOut());
3892 m_body += m_state.accumulatorVariableOut;
3894 const QString explicitCast
3895 = originalOut.isStoredIn(m_typeResolver->stringType())
3896 ? originalOut.storedType()->internalName()
3898 m_body += conversion(
3899 originalOut, m_state.accumulatorOut(),
3900 explicitCast + u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
3904void QQmlJSCodeGenerator::generateArithmeticConstOperation(
int rhsConst,
const QString &cppOperator)
3906 generateArithmeticOperation(
3907 conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3908 consumedAccumulatorVariableIn()),
3909 conversion(literalType(m_typeResolver->int32Type()),
3910 m_state.readAccumulator(), QString::number(rhsConst)),
3914void QQmlJSCodeGenerator::generateUnaryOperation(
const QString &cppOperator)
3916 const auto var = conversion(m_state.accumulatorIn(),
3917 originalType(m_state.readAccumulator()),
3918 consumedAccumulatorVariableIn());
3920 if (var == m_state.accumulatorVariableOut) {
3921 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3925 const auto originalResult = originalType(m_state.accumulatorOut());
3926 if (m_state.accumulatorOut() == originalResult) {
3927 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3928 m_body += m_state.accumulatorVariableOut + u" = "_s
3929 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3933 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3934 originalResult, m_state.accumulatorOut(), cppOperator + var) + u";\n"_s;
3937void QQmlJSCodeGenerator::generateInPlaceOperation(
const QString &cppOperator)
3941 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3942 m_state.accumulatorVariableIn);
3943 if (var == m_state.accumulatorVariableOut) {
3944 m_body += cppOperator + var + u";\n"_s;
3949 const QString var = conversion(m_state.accumulatorIn(), m_state.readAccumulator(),
3950 consumedAccumulatorVariableIn());
3952 const auto originalResult = originalType(m_state.accumulatorOut());
3953 if (m_state.accumulatorOut() == originalResult) {
3954 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3955 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3960 m_body += u"auto converted = "_s + var + u";\n"_s;
3961 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3962 originalResult, m_state.accumulatorOut(), u'('
3963 + cppOperator + u"converted)"_s) + u";\n"_s;
3967void QQmlJSCodeGenerator::generateLookup(
const QString &lookup,
const QString &initialization,
3968 const QString &resultPreparation)
3970 m_body += u"#ifndef QT_NO_DEBUG\n"_s;
3971 generateSetInstructionPointer();
3972 m_body += u"#endif\n"_s;
3974 if (!resultPreparation.isEmpty())
3975 m_body += resultPreparation + u";\n"_s;
3976 m_body += u"while (!"_s + lookup + u") {\n"_s;
3978 m_body += u"#ifdef QT_NO_DEBUG\n"_s;
3979 generateSetInstructionPointer();
3980 m_body += u"#endif\n"_s;
3982 m_body += initialization + u";\n"_s;
3983 generateExceptionCheck();
3984 if (!resultPreparation.isEmpty())
3985 m_body += resultPreparation + u";\n"_s;
3989void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(
int relativeOffset)
3991 QString conversionCode;
3992 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
3993 const auto annotation = m_annotations.find(absoluteOffset);
3994 if (
static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
3995 const auto &conversions = annotation->second.typeConversions;
3997 for (
auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
3998 regIt != regEnd; ++regIt) {
3999 const QQmlJSRegisterContent targetType = regIt.value().content;
4000 if (!targetType.isValid() || !isTypeStorable(m_typeResolver, targetType.storedType()))
4003 const int registerIndex = regIt.key();
4004 const auto variable = m_registerVariables.constFind(targetType);
4006 if (variable == m_registerVariables.constEnd())
4009 QQmlJSRegisterContent currentType;
4010 QString currentVariable;
4011 if (registerIndex == m_state.changedRegisterIndex()) {
4012 currentVariable = changedRegisterVariable();
4013 if (variable->variableName == currentVariable)
4016 currentType = m_state.changedRegister();
4021 const auto it = m_state.registers.find(registerIndex);
4022 if (it == m_state.registers.end()
4023 || variable->variableName == registerVariable(registerIndex)) {
4027 currentType = it.value().content;
4028 currentVariable = consumedRegisterVariable(registerIndex);
4032 if (currentType == targetType)
4035 conversionCode += variable->variableName;
4036 conversionCode += u" = "_s;
4037 conversionCode += conversion(currentType, targetType, currentVariable);
4038 conversionCode += u";\n"_s;
4042 if (relativeOffset) {
4043 auto labelIt = m_labels.find(absoluteOffset);
4044 if (labelIt == m_labels.end())
4045 labelIt = m_labels.insert(absoluteOffset, u"label_%1"_s.arg(m_labels.size()));
4046 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
4049 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
4052QString QQmlJSCodeGenerator::registerVariable(
int index)
const
4054 QQmlJSRegisterContent content = registerType(index);
4055 const auto it = m_registerVariables.constFind(content);
4056 if (it != m_registerVariables.constEnd())
4057 return it->variableName;
4062QString QQmlJSCodeGenerator::lookupVariable(
int lookupIndex)
const
4064 for (
auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
4065 if (it.key().resultLookupIndex() == lookupIndex)
4066 return it->variableName;
4071QString QQmlJSCodeGenerator::consumedRegisterVariable(
int index)
const
4073 const QString var = registerVariable(index);
4074 if (var.isEmpty() || !shouldMoveRegister(index))
4076 return u"std::move(" + var + u")";
4079QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn()
const
4081 return shouldMoveRegister(Accumulator)
4082 ? u"std::move(" + m_state.accumulatorVariableIn + u")"
4083 : m_state.accumulatorVariableIn;
4086QString QQmlJSCodeGenerator::changedRegisterVariable()
const
4088 QQmlJSRegisterContent changedRegister = m_state.changedRegister();
4090 const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
4091 if (storedType.isNull())
4094 return m_registerVariables.value(changedRegister).variableName;
4097QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(
int index)
const
4099 auto it = m_state.registers.find(index);
4100 if (it != m_state.registers.end())
4101 return it.value().content;
4103 return QQmlJSRegisterContent();
4106QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(
int lookupIndex)
const
4108 auto it = m_state.lookups.find(lookupIndex);
4109 if (it != m_state.lookups.end())
4110 return it.value().content;
4112 return QQmlJSRegisterContent();
4115bool QQmlJSCodeGenerator::shouldMoveRegister(
int index)
const
4117 return m_state.canMoveReadRegister(index)
4118 && !m_typeResolver->isTriviallyCopyable(m_state.readRegister(index).storedType());
4121QString QQmlJSCodeGenerator::conversion(
4122 QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4124 const QQmlJSScope::ConstPtr contained = to.containedType();
4127 if (from.isStoredIn(m_typeResolver->jsPrimitiveType()) && m_typeResolver->isPrimitive(to)) {
4129 QString primitive = [&]() -> QString {
4130 if (contained == m_typeResolver->jsPrimitiveType())
4133 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
4134 if (contained == m_typeResolver->boolType())
4135 return conversion.arg(u"Boolean"_s);
4136 if (m_typeResolver->isIntegral(to))
4137 return conversion.arg(u"Integer"_s);
4138 if (m_typeResolver->isNumeric(to))
4139 return conversion.arg(u"Double"_s);
4140 if (contained == m_typeResolver->stringType())
4141 return conversion.arg(u"String"_s);
4143 u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
4146 if (primitive.isEmpty())
4149 return convertStored(m_typeResolver->jsPrimitiveType(), to.storedType(), primitive);
4152 if (to.isStoredIn(contained)
4153 || m_typeResolver->isNumeric(to.storedType())
4154 || to.storedType()->isReferenceType()
4155 || from.contains(contained)) {
4162 return convertStored(from.storedType(), to.storedType(), variable);
4164 return convertContained(from, to, variable);
4168QString QQmlJSCodeGenerator::convertStored(
4169 const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to,
const QString &variable)
4173 Q_ASSERT(!to->isComposite());
4175 const auto jsValueType = m_typeResolver->jsValueType();
4176 const auto varType = m_typeResolver->varType();
4177 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
4178 const auto boolType = m_typeResolver->boolType();
4180 auto zeroBoolOrInt = [&](
const QQmlJSScope::ConstPtr &to) {
4183 if (m_typeResolver->isSignedInteger(to))
4185 if (m_typeResolver->isUnsignedInteger(to))
4190 if (from == m_typeResolver->voidType()) {
4191 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4192 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4193 const QString zero = zeroBoolOrInt(to);
4194 if (!zero.isEmpty())
4196 if (to == m_typeResolver->floatType())
4197 return u"std::numeric_limits<float>::quiet_NaN()"_s;
4198 if (to == m_typeResolver->realType())
4199 return u"std::numeric_limits<double>::quiet_NaN()"_s;
4200 if (to == m_typeResolver->stringType())
4201 return QQmlJSUtils::toLiteral(u"undefined"_s);
4202 if (to == m_typeResolver->varType())
4203 return u"QVariant()"_s;
4204 if (to == m_typeResolver->jsValueType())
4205 return u"QJSValue();"_s;
4206 if (to == m_typeResolver->jsPrimitiveType())
4207 return u"QJSPrimitiveValue()"_s;
4212 if (from == m_typeResolver->nullType()) {
4213 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4214 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4215 if (to == jsValueType)
4216 return u"QJSValue(QJSValue::NullValue)"_s;
4217 if (to == jsPrimitiveType)
4218 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
4220 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
4221 const QString zero = zeroBoolOrInt(to);
4222 if (!zero.isEmpty())
4224 if (to == m_typeResolver->floatType())
4226 if (to == m_typeResolver->realType())
4228 if (to == m_typeResolver->stringType())
4229 return QQmlJSUtils::toLiteral(u"null"_s);
4232 REJECT<QString>(u"Conversion from null to %1"_s.arg(to->internalName()));
4238 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4239 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4244 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
4246 if (base->internalName() == to->internalName())
4247 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4249 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
4250 if (base->internalName() == from->internalName())
4251 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4253 }
else if (to == m_typeResolver->boolType()) {
4254 return u'(' + variable + u" != nullptr)"_s;
4258 auto isJsValue = [&](
const QQmlJSScope::ConstPtr &candidate) {
4259 return candidate == jsValueType || candidate->isScript();
4262 if (isJsValue(from) && isJsValue(to))
4265 const auto isBoolOrNumber = [&](
const QQmlJSScope::ConstPtr &type) {
4266 return m_typeResolver->isNumeric(type)
4267 || type == m_typeResolver->boolType()
4268 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
4271 if (from == m_typeResolver->realType() || from == m_typeResolver->floatType()) {
4272 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type()) {
4273 return to->internalName() + u"(QJSNumberCoercion::roundTowards0("_s
4274 + variable + u"))"_s;
4277 if (m_typeResolver->isSignedInteger(to))
4278 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
4279 if (m_typeResolver->isUnsignedInteger(to))
4280 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
4281 if (to == m_typeResolver->boolType())
4282 return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
4285 if (isBoolOrNumber(from) && isBoolOrNumber(to))
4286 return to->internalName() + u'(' + variable + u')';
4289 if (from == jsPrimitiveType) {
4290 if (to == m_typeResolver->realType())
4291 return variable + u".toDouble()"_s;
4293 return variable + u".toBoolean()"_s;
4294 if (to == m_typeResolver->int64Type() || to == m_typeResolver->uint64Type())
4295 return u"%1(%2.toDouble())"_s.arg(to->internalName(), variable);
4296 if (m_typeResolver->isIntegral(to))
4297 return u"%1(%2.toInteger())"_s.arg(to->internalName(), variable);
4298 if (to == m_typeResolver->stringType())
4299 return variable + u".toString()"_s;
4300 if (to == jsValueType)
4301 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
4303 return variable + u".toVariant()"_s;
4304 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4305 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4308 if (isJsValue(from)) {
4309 if (to == jsPrimitiveType)
4310 return variable + u".toPrimitive()"_s;
4312 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
4313 return u"qjsvalue_cast<"_s + castTargetName(to) + u">("_s + variable + u')';
4316 if (to == jsPrimitiveType) {
4318 Q_ASSERT(from != m_typeResolver->nullType());
4319 Q_ASSERT(from != m_typeResolver->voidType());
4321 if (from == m_typeResolver->boolType()
4322 || from == m_typeResolver->int32Type()
4323 || from == m_typeResolver->realType()
4324 || from == m_typeResolver->stringType()) {
4325 return u"QJSPrimitiveValue("_s + variable + u')';
4326 }
else if (from == m_typeResolver->int16Type()
4327 || from == m_typeResolver->int8Type()
4328 || from == m_typeResolver->uint16Type()
4329 || from == m_typeResolver->uint8Type()) {
4330 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
4331 }
else if (m_typeResolver->isNumeric(from)) {
4332 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
4336 if (to == jsValueType)
4337 return u"aotContext->engine->toScriptValue("_s + variable + u')';
4339 if (from == varType) {
4340 if (to == m_typeResolver->listPropertyType())
4341 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
4342 return u"aotContext->engine->fromVariant<"_s + castTargetName(to) + u">("_s
4347 return u"QVariant::fromValue("_s + variable + u')';
4349 if (from == m_typeResolver->urlType() && to == m_typeResolver->stringType())
4350 return variable + u".toString()"_s;
4352 if (from == m_typeResolver->stringType() && to == m_typeResolver->urlType())
4353 return u"QUrl("_s + variable + u')';
4355 if (from == m_typeResolver->byteArrayType() && to == m_typeResolver->stringType())
4356 return u"QString::fromUtf8("_s + variable + u')';
4358 if (from == m_typeResolver->stringType() && to == m_typeResolver->byteArrayType())
4359 return variable + u".toUtf8()"_s;
4361 for (
const auto &originType : {
4362 m_typeResolver->dateTimeType(),
4363 m_typeResolver->dateType(),
4364 m_typeResolver->timeType()}) {
4365 if (from == originType) {
4366 for (
const auto &targetType : {
4367 m_typeResolver->dateTimeType(),
4368 m_typeResolver->dateType(),
4369 m_typeResolver->timeType(),
4370 m_typeResolver->stringType(),
4371 m_typeResolver->realType()}) {
4372 if (to == targetType) {
4373 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
4374 originType->internalName(), targetType->internalName(), variable);
4381 const auto retrieveFromPrimitive = [&](
4382 const QQmlJSScope::ConstPtr &type,
const QString &expression) -> QString
4384 if (type == m_typeResolver->boolType())
4385 return expression + u".toBoolean()"_s;
4386 if (m_typeResolver->isSignedInteger(type))
4387 return expression + u".toInteger()"_s;
4388 if (m_typeResolver->isUnsignedInteger(type))
4389 return u"uint("_s + expression + u".toInteger())"_s;
4390 if (type == m_typeResolver->realType())
4391 return expression + u".toDouble()"_s;
4392 if (type == m_typeResolver->floatType())
4393 return u"float("_s + expression + u".toDouble())"_s;
4394 if (type == m_typeResolver->stringType())
4395 return expression + u".toString()"_s;
4399 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
4400 const QString retrieve = retrieveFromPrimitive(
4401 to, convertStored(from, m_typeResolver->jsPrimitiveType(), variable));
4402 if (!retrieve.isEmpty())
4406 if (from->isReferenceType() && to == m_typeResolver->stringType()) {
4407 return u"aotContext->engine->coerceValue<"_s + castTargetName(from) + u", "
4408 + castTargetName(to) + u">("_s + variable + u')';
4412 if (to == m_typeResolver->boolType()) {
4414 Q_ASSERT(from != m_typeResolver->nullType());
4415 Q_ASSERT(from != m_typeResolver->voidType());
4416 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
4417 Q_ASSERT(!isBoolOrNumber(from));
4422 if (m_typeResolver->areEquivalentLists(from, to))
4425 if (from->isListProperty()
4426 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
4427 && to->elementType()->isReferenceType()
4428 && !to->isListProperty()) {
4429 return variable + u".toList<"_s + to->internalName() + u">()"_s;
4432 bool isExtension =
false;
4433 if (m_typeResolver->canPopulate(to, from, &isExtension)) {
4435 u"populating "_s + to->internalName() + u" from "_s + from->internalName());
4436 }
else if (
const auto ctor = m_typeResolver->selectConstructor(to, from, &isExtension);
4438 const auto argumentTypes = ctor.parameters();
4439 return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
4440 + u"("_s + convertStored(from, argumentTypes[0].type(), variable) + u")"_s;
4443 if (to == m_typeResolver->stringType()
4444 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
4445 addInclude(u"QtQml/qjslist.h"_s);
4449 return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
4456 u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
4459QString QQmlJSCodeGenerator::convertContained(QQmlJSRegisterContent from, QQmlJSRegisterContent to,
const QString &variable)
4461 const QQmlJSScope::ConstPtr containedFrom = from.containedType();
4462 const QQmlJSScope::ConstPtr containedTo = to.containedType();
4465 Q_ASSERT(!to.storedType()->isReferenceType());
4466 Q_ASSERT(!to.isStoredIn(containedTo));
4467 Q_ASSERT(containedFrom != containedTo);
4469 if (!to.isStoredIn(m_typeResolver->varType())
4470 && !to.isStoredIn(m_typeResolver->jsPrimitiveType())) {
4471 REJECT<QString>(u"internal conversion into unsupported wrapper type."_s);
4474 bool isExtension =
false;
4475 if (m_typeResolver->canPopulate(containedTo, containedFrom, &isExtension)) {
4476 REJECT<QString>(u"populating "_s + containedTo->internalName()
4477 + u" from "_s + containedFrom->internalName());
4478 }
else if (
const auto ctor = m_typeResolver->selectConstructor(
4479 containedTo, containedFrom, &isExtension); ctor.isValid()) {
4480 return generateCallConstructor(
4481 ctor, {from}, {variable}, metaType(containedTo),
4482 metaObject(isExtension ? containedTo->extensionType().scope : containedTo));
4485 const auto originalFrom = originalType(from);
4486 const auto containedOriginalFrom = originalFrom.containedType();
4487 if (containedFrom != containedOriginalFrom
4488 && m_typeResolver->canHold(containedFrom, containedOriginalFrom)) {
4491 return conversion(m_pool->storedIn(originalFrom, from.storedType()), to, variable);
4494 if (m_typeResolver->isPrimitive(containedFrom) && m_typeResolver->isPrimitive(containedTo)) {
4495 const QQmlJSRegisterContent intermediate
4496 = m_pool->storedIn(from, m_typeResolver->jsPrimitiveType());
4497 return conversion(intermediate, to, conversion(from, intermediate, variable));
4501 u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
4502 .arg(from.descriptiveName(), to.descriptiveName()));
4505void QQmlJSCodeGenerator::reject(
const QString &thing)
4507 addError(u"Cannot generate efficient code for %1"_s.arg(thing));
4511void QQmlJSCodeGenerator::skip(
const QString &thing)
4513 addSkip(u"Skipped code generation for %1"_s.arg(thing));
4516QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
4517 : accumulatorOut(generator->m_state.accumulatorOut())
4518 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
4519 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
4520 , generator(generator)
4522 if (accumulatorVariableOut.isEmpty())
4525 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
4526 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(accumulatorOut);
4527 const QQmlJSRegisterContent storage = accumulatorOut.storage();
4528 const QQmlJSScope::ConstPtr stored = storage.containedType();
4529 const QQmlJSScope::ConstPtr origStored = resolver->original(storage).containedType();
4534 if (origStored != stored
4535 || (origContained != accumulatorOut.containedType() && stored == resolver->varType())) {
4537 const bool storable = isTypeStorable(resolver, origStored);
4538 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
4539 generator->m_state.setRegister(Accumulator, generator->originalType(accumulatorOut));
4540 generator->m_body += u"{\n"_s;
4542 generator->m_body += origStored->augmentedInternalName() + u' '
4543 + generator->m_state.accumulatorVariableOut + u";\n";
4545 }
else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
4546 && generator->m_state.readsRegister(Accumulator)
4547 && generator->m_state.accumulatorOut().isStoredIn(resolver->varType())) {
4551 generator->m_state.accumulatorVariableIn
4552 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
4553 generator->m_body += u"{\n"_s;
4554 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
4555 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
4559QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
4561 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
4562 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
4563 generator->m_state.accumulatorOut(), accumulatorOut,
4564 u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
4565 generator->m_body += u"}\n"_s;
4566 generator->m_state.setRegister(Accumulator, accumulatorOut);
4567 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
4568 }
else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
4569 generator->m_body += u"}\n"_s;
4570 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
Combined button and popup list for selecting options.
static QString toNumericString(double value)
static QString messageTypeForMethod(const QString &method)
static QString derefContentPointer(const QString &contentPointer)
static bool canTypeBeAffectedBySideEffects(const QQmlJSTypeResolver *typeResolver, const QQmlJSRegisterContent &baseType)
#define BYTECODE_UNIMPLEMENTED()
#define INJECT_TRACE_INFO(function)
static QString registerName(int registerIndex, int offset)
static QString minExpression(int argc)
static QString maxExpression(int argc)
static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)