1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qqmljscodegenerator_p.h"
5#include "qqmljsmetatypes_p.h"
6#include "qqmljsregistercontent_p.h"
7#include "qqmljsscope_p.h"
8#include "qqmljsutils_p.h"
9
10#include <private/qqmljstypepropagator_p.h>
11
12#include <private/qqmlirbuilder_p.h>
13#include <private/qqmljsscope_p.h>
14#include <private/qqmljsutils_p.h>
15#include <private/qv4compilerscanfunctions_p.h>
16#include <private/qduplicatetracker_p.h>
17
18#include <QtCore/qdir.h>
19#include <QtCore/qfileinfo.h>
20
21QT_BEGIN_NAMESPACE
22
23using namespace Qt::StringLiterals;
24
25/*!
26 * \internal
27 * \class QQmlJSCodeGenerator
28 *
29 * This is a final compile pass that generates C++ code from a function and the
30 * annotations produced by previous passes. Such annotations are produced by
31 * QQmlJSTypePropagator, and possibly amended by other passes.
32 */
33
34#define BYTECODE_UNIMPLEMENTED() Q_ASSERT_X(false, Q_FUNC_INFO, "not implemented");
35
36#define INJECT_TRACE_INFO(function) \
37 static const bool injectTraceInfo = true; \
38 if (injectTraceInfo) { \
39 m_body += u"// "_s + QStringLiteral(#function) + u'\n'; \
40 }
41
42
43static bool isTypeStorable(const QQmlJSTypeResolver *resolver, const QQmlJSScope::ConstPtr &type)
44{
45 return !type.isNull()
46 && !resolver->equals(a: type, b: resolver->nullType())
47 && !resolver->equals(a: type, b: resolver->voidType());
48}
49
50QString QQmlJSCodeGenerator::castTargetName(const QQmlJSScope::ConstPtr &type) const
51{
52 return type->augmentedInternalName();
53}
54
55QQmlJSCodeGenerator::QQmlJSCodeGenerator(const QV4::Compiler::Context *compilerContext,
56 const QV4::Compiler::JSUnitGenerator *unitGenerator,
57 const QQmlJSTypeResolver *typeResolver,
58 QQmlJSLogger *logger, BasicBlocks basicBlocks,
59 InstructionAnnotations annotations)
60 : QQmlJSCompilePass(unitGenerator, typeResolver, logger, basicBlocks, annotations)
61 , m_context(compilerContext)
62{}
63
64QString QQmlJSCodeGenerator::metaTypeFromType(const QQmlJSScope::ConstPtr &type) const
65{
66 return u"QMetaType::fromType<"_s + type->augmentedInternalName() + u">()"_s;
67}
68
69QString QQmlJSCodeGenerator::metaTypeFromName(const QQmlJSScope::ConstPtr &type) const
70{
71 return u"[]() { static const auto t = QMetaType::fromName(\""_s
72 + QString::fromUtf8(ba: QMetaObject::normalizedType(type: type->augmentedInternalName().toUtf8()))
73 + u"\"); return t; }()"_s;
74}
75
76QString QQmlJSCodeGenerator::compositeListMetaType(const QString &elementName) const
77{
78 return u"QQmlPrivate::compositeListMetaType(aotContext->compilationUnit, "_s
79 + (m_jsUnitGenerator->hasStringId(string: elementName)
80 ? QString::number(m_jsUnitGenerator->getStringId(string: elementName)) + u')'
81 : u"QStringLiteral(\"%1\"))"_s.arg(a: elementName));
82}
83
84QString QQmlJSCodeGenerator::compositeMetaType(const QString &elementName) const
85{
86 return u"QQmlPrivate::compositeMetaType(aotContext->compilationUnit, "_s
87 + (m_jsUnitGenerator->hasStringId(string: elementName)
88 ? QString::number(m_jsUnitGenerator->getStringId(string: elementName)) + u')'
89 : u"QStringLiteral(\"%1\"))"_s.arg(a: elementName));
90}
91
92QString QQmlJSCodeGenerator::metaObject(const QQmlJSScope::ConstPtr &objectType)
93{
94 if (objectType->isComposite()) {
95 const QString name = m_typeResolver->nameForType(type: objectType);
96 if (name.isEmpty()) {
97 reject(thing: u"retrieving the metaObject of a composite type without an element name."_s);
98 return QString();
99 }
100 return compositeMetaType(elementName: name) + u".metaObject()"_s;
101 }
102
103 if (objectType->internalName() == u"QObject"_s
104 || objectType->internalName() == u"QQmlComponent"_s) {
105 return u'&' + objectType->internalName() + u"::staticMetaObject"_s;
106 }
107 return metaTypeFromName(type: objectType) + u".metaObject()"_s;
108}
109
110QString QQmlJSCodeGenerator::metaType(const QQmlJSScope::ConstPtr &type)
111{
112 if (type->isComposite()) {
113 const QString name = m_typeResolver->nameForType(type);
114 if (name.isEmpty()) {
115 reject(thing: u"retrieving the metaType of a composite type without an element name."_s);
116 return QString();
117 }
118 return compositeMetaType(elementName: name);
119 }
120
121 if (type->isListProperty() && type->valueType()->isComposite()) {
122 const QString name = m_typeResolver->nameForType(type: type->valueType());
123 Q_ASSERT(!name.isEmpty()); // There can't be a list with anonymous composite value type
124 return compositeListMetaType(elementName: name);
125 }
126
127 return m_typeResolver->equals(a: m_typeResolver->genericType(type), b: type)
128 ? metaTypeFromType(type)
129 : metaTypeFromName(type);
130}
131
132QQmlJSAotFunction QQmlJSCodeGenerator::run(const Function *function,
133 QQmlJS::DiagnosticMessage *error,
134 bool basicBlocksValidationFailed)
135{
136 m_function = function;
137 m_error = error;
138
139 QHash<int, int> numRegisterVariablesPerIndex;
140
141 const auto addVariable
142 = [&](int registerIndex, int lookupIndex, const QQmlJSScope::ConstPtr &seenType) {
143 // Don't generate any variables for registers that are initialized with undefined.
144 if (registerIndex == InvalidRegister || !isTypeStorable(resolver: m_typeResolver, type: seenType))
145 return;
146
147 const RegisterVariablesKey key = { .internalName: seenType->internalName(), .registerIndex: registerIndex, .lookupIndex: lookupIndex };
148
149
150 const auto oldSize = m_registerVariables.size();
151 auto &e = m_registerVariables[key];
152 if (m_registerVariables.size() != oldSize) {
153 e.variableName = u"r%1_%2"_s
154 .arg(a: registerIndex)
155 .arg(a: numRegisterVariablesPerIndex[registerIndex]++);
156 e.storedType = m_typeResolver->comparableType(type: seenType);
157 }
158 ++e.numTracked;
159 };
160
161QT_WARNING_PUSH
162QT_WARNING_DISABLE_CLANG("-Wrange-loop-analysis")
163 for (const auto &annotation : m_annotations) {
164 addVariable(annotation.second.changedRegisterIndex,
165 annotation.second.changedRegister.resultLookupIndex(),
166 annotation.second.changedRegister.storedType());
167 for (auto it = annotation.second.typeConversions.begin(),
168 end = annotation.second.typeConversions.end();
169 it != end; ++it) {
170 addVariable(
171 it.key(), it.value().content.resultLookupIndex(),
172 it.value().content.storedType());
173 }
174 }
175QT_WARNING_POP
176
177 // ensure we have m_labels for loops
178 for (const auto loopLabel : m_context->labelInfo)
179 m_labels.insert(key: loopLabel, value: u"label_%1"_s.arg(a: m_labels.size()));
180
181 // Initialize the first instruction's state to hold the arguments.
182 // After this, the arguments (or whatever becomes of them) are carried
183 // over into any further basic blocks automatically.
184 m_state.State::operator=(initialState(function: m_function));
185
186 const QByteArray byteCode = function->code;
187 decode(code: byteCode.constData(), len: static_cast<uint>(byteCode.size()));
188
189 QQmlJSAotFunction result;
190 result.includes.swap(other&: m_includes);
191
192 if (basicBlocksValidationFailed) {
193 result.code += "// QV4_BASIC_BLOCK_VALIDATION_FAILED: This file failed compilation "_L1
194 "with basic blocks validation but compiled without it.\n"_L1;
195 }
196
197 result.code += u"// %1 at line %2, column %3\n"_s
198 .arg(a: m_context->name).arg(a: m_context->line).arg(a: m_context->column);
199
200 for (auto registerIt = m_registerVariables.cbegin(), registerEnd = m_registerVariables.cend();
201 registerIt != registerEnd; ++registerIt) {
202
203 const int registerIndex = registerIt.key().registerIndex;
204 const bool registerIsArgument = isArgument(registerIndex);
205
206 result.code += registerIt.key().internalName;
207
208 const QQmlJSScope::ConstPtr storedType = registerIt->storedType;
209 const bool isPointer
210 = (storedType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
211 if (isPointer)
212 result.code += u" *"_s;
213 else
214 result.code += u' ';
215
216 if (!registerIsArgument
217 && registerIndex != Accumulator
218 && registerIndex != This
219 && !m_typeResolver->registerIsStoredIn(
220 reg: function->registerTypes[registerIndex - firstRegisterIndex()],
221 type: m_typeResolver->voidType())) {
222 result.code += registerIt->variableName + u" = "_s;
223 result.code += convertStored(from: m_typeResolver->voidType(), to: storedType, variable: QString());
224 } else if (registerIsArgument && m_typeResolver->registerIsStoredIn(
225 reg: argumentType(registerIndex), type: storedType)) {
226 const int argumentIndex = registerIndex - FirstArgument;
227 const QQmlJSRegisterContent argument
228 = m_function->argumentTypes[argumentIndex];
229 const QQmlJSRegisterContent original
230 = m_typeResolver->original(type: argument);
231
232 const bool needsConversion = argument != original;
233 if (!isPointer && registerIt->numTracked == 1 && !needsConversion) {
234 // Not a pointer, never written to, and doesn't need any initial conversion.
235 // This is a readonly argument.
236 //
237 // We would like to make the variable a const ref if it's a readonly argument,
238 // but due to the various call interfaces accepting non-const values, we can't.
239 // We rely on those calls to still not modify their arguments in place.
240 result.code += u'&';
241 }
242
243 result.code += registerIt->variableName + u" = "_s;
244
245 const auto originalContained = m_typeResolver->originalContainedType(container: argument);
246 QString originalValue;
247 const bool needsQVariantWrapping = !m_typeResolver->globalType(type: storedType).isList()
248 && !originalContained->isReferenceType()
249 && m_typeResolver->equals(a: storedType, b: m_typeResolver->varType())
250 && !m_typeResolver->equals(a: originalContained, b: m_typeResolver->varType());
251 if (needsQVariantWrapping) {
252 originalValue = u"QVariant(%1, argv[%2])"_s.arg(a: metaTypeFromName(type: originalContained))
253 .arg(a: QString::number(argumentIndex + 1));
254 } else {
255 originalValue = u"(*static_cast<"_s + castTargetName(type: original.storedType())
256 + u"*>(argv["_s + QString::number(argumentIndex + 1) + u"]))"_s;
257 }
258
259 if (needsConversion)
260 result.code += conversion(from: original, to: argument, variable: originalValue);
261 else
262 result.code += originalValue;
263 } else {
264 result.code += registerIt->variableName;
265 }
266 result.code += u";\n"_s;
267 }
268
269 result.code += m_body;
270
271
272 QString signature
273 = u" struct { QV4::ExecutableCompilationUnit *compilationUnit; } c { unit };\n"
274 " const auto *aotContext = &c;\n"
275 " Q_UNUSED(aotContext);\n"_s;
276
277 if (function->returnType.isValid()) {
278 signature += u" argTypes[0] = %1;\n"_s.arg(
279 a: metaType(type: m_typeResolver->containedType(container: function->returnType)));
280 } else {
281 signature += u" argTypes[0] = QMetaType();\n"_s;
282 }
283 result.numArguments = function->argumentTypes.length();
284 for (qsizetype i = 0; i != result.numArguments; ++i) {
285 signature += u" argTypes[%1] = %2;\n"_s.arg(
286 args: QString::number(i + 1),
287 args: metaType(type: m_typeResolver->originalContainedType(container: function->argumentTypes[i])));
288 }
289
290 result.signature = signature;
291 return result;
292}
293
294void QQmlJSCodeGenerator::generateReturnError()
295{
296 const auto finalizeReturn = qScopeGuard(f: [this]() { m_body += u"return;\n"_s; });
297
298 m_body += u"aotContext->setReturnValueUndefined();\n"_s;
299 const auto ret = m_function->returnType;
300 if (!ret.isValid() || m_typeResolver->registerContains(reg: ret, type: m_typeResolver->voidType()))
301 return;
302
303 m_body += u"if (argv[0]) {\n"_s;
304
305 const auto contained = m_typeResolver->containedType(container: ret);
306 const auto stored = ret.storedType();
307 if (contained->isReferenceType() && stored->isReferenceType()) {
308 m_body += u" *static_cast<"_s
309 + stored->augmentedInternalName()
310 + u" *>(argv[0]) = nullptr;\n"_s;
311 } else if (m_typeResolver->equals(a: contained, b: stored)) {
312 m_body += u" *static_cast<"_s + stored->internalName() + u" *>(argv[0]) = "_s
313 + stored->internalName() + u"();\n"_s;
314 } else {
315 m_body += u" const QMetaType returnType = "_s
316 + metaType(type: m_typeResolver->containedType(container: ret)) + u";\n"_s;
317 m_body += u" returnType.destruct(argv[0]);\n"_s;
318 m_body += u" returnType.construct(argv[0]);\n "_s;
319 }
320
321 m_body += u"}\n"_s;
322}
323
324void QQmlJSCodeGenerator::generate_Ret()
325{
326 INJECT_TRACE_INFO(generate_Ret);
327
328 const auto finalizeReturn = qScopeGuard(f: [this]() {
329 m_body += u"return;\n"_s;
330 m_skipUntilNextLabel = true;
331 resetState();
332 });
333
334 if (!m_function->returnType.isValid())
335 return;
336
337 m_body += u"if (argv[0]) {\n"_s;
338
339 const QString signalUndefined = u"aotContext->setReturnValueUndefined();\n"_s;
340 const QString in = m_state.accumulatorVariableIn;
341
342 if (in.isEmpty()) {
343 if (m_typeResolver->equals(a: m_state.accumulatorIn().storedType(),
344 b: m_typeResolver->voidType())) {
345 m_body += signalUndefined;
346
347 }
348 } else if (m_typeResolver->registerIsStoredIn(
349 reg: m_state.accumulatorIn(), type: m_typeResolver->varType())) {
350 m_body += u" if (!"_s + in + u".isValid())\n"_s;
351 m_body += u" "_s + signalUndefined;
352 } else if (m_typeResolver->registerIsStoredIn(
353 reg: m_state.accumulatorIn(), type: m_typeResolver->jsPrimitiveType())) {
354 m_body += u" if ("_s + in + u".type() == QJSPrimitiveValue::Undefined)\n"_s;
355 m_body += u" "_s + signalUndefined;
356 } else if (m_typeResolver->registerIsStoredIn(
357 reg: m_state.accumulatorIn(), type: m_typeResolver->jsValueType())) {
358 m_body += u" if ("_s + in + u".isUndefined())\n"_s;
359 m_body += u" "_s + signalUndefined;
360 }
361
362 if (m_typeResolver->registerContains(
363 reg: m_function->returnType, type: m_typeResolver->voidType())) {
364 m_body += u"}\n"_s;
365 return;
366 }
367
368 const auto contained = m_typeResolver->containedType(container: m_function->returnType);
369 const auto stored = m_function->returnType.storedType();
370 if (m_typeResolver->equals(a: contained, b: stored)
371 || (contained->isReferenceType() && stored->isReferenceType())) {
372 m_body += u" *static_cast<"_s
373 + stored->augmentedInternalName()
374 + u" *>(argv[0]) = "_s
375 + conversion(from: m_state.accumulatorIn(), to: m_function->returnType,
376 variable: consumedAccumulatorVariableIn())
377 + u";\n"_s;
378 } else if (m_typeResolver->registerContains(reg: m_state.accumulatorIn(), type: contained)) {
379 m_body += u" const QMetaType returnType = "_s + contentType(content: m_state.accumulatorIn(), var: in)
380 + u";\n"_s;
381 m_body += u" returnType.destruct(argv[0]);\n"_s;
382 m_body += u" returnType.construct(argv[0], "_s
383 + contentPointer(content: m_state.accumulatorIn(), var: in) + u");\n"_s;
384 } else {
385 m_body += u" const auto converted = "_s
386 + conversion(from: m_state.accumulatorIn(), to: m_function->returnType,
387 variable: consumedAccumulatorVariableIn()) + u";\n"_s;
388 m_body += u" const QMetaType returnType = "_s
389 + contentType(content: m_function->returnType, var: u"converted"_s)
390 + u";\n"_s;
391 m_body += u" returnType.destruct(argv[0]);\n"_s;
392 m_body += u" returnType.construct(argv[0], "_s
393 + contentPointer(content: m_function->returnType, var: u"converted"_s) + u");\n"_s;
394 }
395
396 m_body += u"}\n"_s;
397}
398
399void QQmlJSCodeGenerator::generate_Debug()
400{
401 BYTECODE_UNIMPLEMENTED();
402}
403
404static QString toNumericString(double value)
405{
406 if (value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max()) {
407 const int i = value;
408 if (i == value)
409 return QString::number(i);
410 }
411
412 switch (qFpClassify(val: value)) {
413 case FP_INFINITE: {
414 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
415 return std::signbit(x: value) ? (u'-' + inf) : inf;
416 }
417 case FP_NAN:
418 return u"std::numeric_limits<double>::quiet_NaN()"_s;
419 case FP_ZERO:
420 return std::signbit(x: value) ? u"-0.0"_s : u"0"_s;
421 default:
422 break;
423 }
424
425 return QString::number(value, format: 'f', precision: std::numeric_limits<double>::max_digits10);
426}
427
428void QQmlJSCodeGenerator::generate_LoadConst(int index)
429{
430 INJECT_TRACE_INFO(generate_LoadConst);
431
432 // You cannot actually get it to generate LoadConst for anything but double. We have
433 // a numer of specialized instructions for the other types, after all. However, let's
434 // play it safe.
435
436 const QV4::ReturnedValue encodedConst = m_jsUnitGenerator->constant(idx: index);
437 const QV4::StaticValue value = QV4::StaticValue::fromReturnedValue(val: encodedConst);
438 const QQmlJSScope::ConstPtr type = m_typeResolver->typeForConst(rv: encodedConst);
439
440 m_body += m_state.accumulatorVariableOut + u" = "_s;
441 if (type == m_typeResolver->realType()) {
442 m_body += conversion(
443 from: type, to: m_state.accumulatorOut(),
444 variable: toNumericString(value: value.doubleValue()));
445 } else if (type == m_typeResolver->int32Type()) {
446 m_body += conversion(
447 from: type, to: m_state.accumulatorOut(),
448 variable: QString::number(value.integerValue()));
449 } else if (type == m_typeResolver->boolType()) {
450 m_body += conversion(
451 from: type, to: m_state.accumulatorOut(),
452 variable: value.booleanValue() ? u"true"_s : u"false"_s);
453 } else if (type == m_typeResolver->voidType()) {
454 m_body += conversion(
455 from: type, to: m_state.accumulatorOut(),
456 variable: QString());
457 } else if (type == m_typeResolver->nullType()) {
458 m_body += conversion(
459 from: type, to: m_state.accumulatorOut(),
460 variable: u"nullptr"_s);
461 } else {
462 reject(thing: u"unsupported constant type"_s);
463 }
464
465 m_body += u";\n"_s;
466}
467
468void QQmlJSCodeGenerator::generate_LoadZero()
469{
470 INJECT_TRACE_INFO(generate_LoadZero);
471
472 m_body += m_state.accumulatorVariableOut;
473 m_body += u" = "_s + conversion(
474 from: m_typeResolver->int32Type(), to: m_state.accumulatorOut(), variable: u"0"_s);
475 m_body += u";\n"_s;
476}
477
478void QQmlJSCodeGenerator::generate_LoadTrue()
479{
480 INJECT_TRACE_INFO(generate_LoadTrue);
481
482 m_body += m_state.accumulatorVariableOut;
483 m_body += u" = "_s + conversion(
484 from: m_typeResolver->boolType(), to: m_state.accumulatorOut(), variable: u"true"_s);
485 m_body += u";\n"_s;
486}
487
488void QQmlJSCodeGenerator::generate_LoadFalse()
489{
490 INJECT_TRACE_INFO(generate_LoadFalse);
491
492 m_body += m_state.accumulatorVariableOut;
493 m_body += u" = "_s + conversion(
494 from: m_typeResolver->boolType(), to: m_state.accumulatorOut(), variable: u"false"_s);
495 m_body += u";\n"_s;
496}
497
498void QQmlJSCodeGenerator::generate_LoadNull()
499{
500 INJECT_TRACE_INFO(generate_LoadNull);
501
502 m_body += m_state.accumulatorVariableOut + u" = "_s;
503 m_body += conversion(from: m_typeResolver->nullType(), to: m_state.accumulatorOut(),
504 variable: u"nullptr"_s);
505 m_body += u";\n"_s;
506}
507
508void QQmlJSCodeGenerator::generate_LoadUndefined()
509{
510 INJECT_TRACE_INFO(generate_LoadUndefined);
511
512 m_body += m_state.accumulatorVariableOut + u" = "_s;
513 m_body += conversion(from: m_typeResolver->voidType(), to: m_state.accumulatorOut(),
514 variable: QString());
515 m_body += u";\n"_s;
516}
517
518void QQmlJSCodeGenerator::generate_LoadInt(int value)
519{
520 INJECT_TRACE_INFO(generate_LoadInt);
521
522 m_body += m_state.accumulatorVariableOut;
523 m_body += u" = "_s;
524 m_body += conversion(from: m_typeResolver->int32Type(), to: m_state.accumulatorOut(),
525 variable: QString::number(value));
526 m_body += u";\n"_s;
527}
528
529void QQmlJSCodeGenerator::generate_MoveConst(int constIndex, int destTemp)
530{
531 INJECT_TRACE_INFO(generate_MoveConst);
532
533 Q_ASSERT(destTemp == m_state.changedRegisterIndex());
534
535 auto var = changedRegisterVariable();
536 if (var.isEmpty())
537 return; // Do not load 'undefined'
538
539 const auto v4Value = QV4::StaticValue::fromReturnedValue(
540 val: m_jsUnitGenerator->constant(idx: constIndex));
541
542 const auto changed = m_state.changedRegister();
543 QQmlJSScope::ConstPtr contained;
544 QString input;
545
546 m_body += var + u" = "_s;
547 if (v4Value.isNull()) {
548 contained = m_typeResolver->nullType();
549 } else if (v4Value.isUndefined()) {
550 contained = m_typeResolver->voidType();
551 } else if (v4Value.isBoolean()) {
552 contained = m_typeResolver->boolType();
553 input = v4Value.booleanValue() ? u"true"_s : u"false"_s;
554 } else if (v4Value.isInteger()) {
555 contained = m_typeResolver->int32Type();
556 input = QString::number(v4Value.int_32());
557 } else if (v4Value.isDouble()) {
558 contained = m_typeResolver->realType();
559 input = toNumericString(value: v4Value.doubleValue());
560 } else {
561 reject(thing: u"unknown const type"_s);
562 return;
563 }
564 m_body += conversion(from: contained, to: changed, variable: input) + u";\n"_s;
565}
566
567void QQmlJSCodeGenerator::generate_LoadReg(int reg)
568{
569 INJECT_TRACE_INFO(generate_LoadReg);
570
571 m_body += m_state.accumulatorVariableOut;
572 m_body += u" = "_s;
573 m_body += conversion(
574 from: registerType(index: reg), to: m_state.accumulatorOut(), variable: consumedRegisterVariable(index: reg));
575 m_body += u";\n"_s;
576}
577
578void QQmlJSCodeGenerator::generate_StoreReg(int reg)
579{
580 INJECT_TRACE_INFO(generate_StoreReg);
581
582 Q_ASSERT(m_state.changedRegisterIndex() == reg);
583 Q_ASSERT(m_state.accumulatorIn().isValid());
584 const QString var = changedRegisterVariable();
585 if (var.isEmpty())
586 return; // don't store "undefined"
587 m_body += var;
588 m_body += u" = "_s;
589 m_body += conversion(from: m_state.accumulatorIn(), to: m_state.changedRegister(),
590 variable: consumedAccumulatorVariableIn());
591 m_body += u";\n"_s;
592}
593
594void QQmlJSCodeGenerator::generate_MoveReg(int srcReg, int destReg)
595{
596 INJECT_TRACE_INFO(generate_MoveReg);
597
598 Q_ASSERT(m_state.changedRegisterIndex() == destReg);
599 const QString destRegName = changedRegisterVariable();
600 if (destRegName.isEmpty())
601 return; // don't store things we cannot store.
602 m_body += destRegName;
603 m_body += u" = "_s;
604 m_body += conversion(
605 from: registerType(index: srcReg), to: m_state.changedRegister(), variable: consumedRegisterVariable(index: srcReg));
606 m_body += u";\n"_s;
607}
608
609void QQmlJSCodeGenerator::generate_LoadImport(int index)
610{
611 Q_UNUSED(index)
612 BYTECODE_UNIMPLEMENTED();
613}
614
615void QQmlJSCodeGenerator::generate_LoadLocal(int index)
616{
617 Q_UNUSED(index);
618 reject(thing: u"LoadLocal"_s);
619}
620
621void QQmlJSCodeGenerator::generate_StoreLocal(int index)
622{
623 Q_UNUSED(index)
624 BYTECODE_UNIMPLEMENTED();
625}
626
627void QQmlJSCodeGenerator::generate_LoadScopedLocal(int scope, int index)
628{
629 Q_UNUSED(scope)
630 Q_UNUSED(index)
631 BYTECODE_UNIMPLEMENTED();
632}
633
634void QQmlJSCodeGenerator::generate_StoreScopedLocal(int scope, int index)
635{
636 Q_UNUSED(scope)
637 Q_UNUSED(index)
638 BYTECODE_UNIMPLEMENTED();
639}
640
641void QQmlJSCodeGenerator::generate_LoadRuntimeString(int stringId)
642{
643 INJECT_TRACE_INFO(generate_LoadRuntimeString);
644
645 m_body += m_state.accumulatorVariableOut;
646 m_body += u" = "_s;
647 m_body += conversion(from: m_typeResolver->stringType(), to: m_state.accumulatorOut(),
648 variable: QQmlJSUtils::toLiteral(s: m_jsUnitGenerator->stringForIndex(index: stringId)));
649 m_body += u";\n"_s;
650}
651
652void QQmlJSCodeGenerator::generate_MoveRegExp(int regExpId, int destReg)
653{
654 Q_UNUSED(regExpId)
655 Q_UNUSED(destReg)
656 BYTECODE_UNIMPLEMENTED();
657}
658
659void QQmlJSCodeGenerator::generate_LoadClosure(int value)
660{
661 Q_UNUSED(value)
662 reject(thing: u"LoadClosure"_s);
663}
664
665void QQmlJSCodeGenerator::generate_LoadName(int nameIndex)
666{
667 Q_UNUSED(nameIndex)
668 reject(thing: u"LoadName"_s);
669}
670
671void QQmlJSCodeGenerator::generate_LoadGlobalLookup(int index)
672{
673 INJECT_TRACE_INFO(generate_LoadGlobalLookup);
674
675 AccumulatorConverter registers(this);
676
677 const QString lookup = u"aotContext->loadGlobalLookup("_s + QString::number(index)
678 + u", &"_s + m_state.accumulatorVariableOut + u", "_s
679 + metaTypeFromType(type: m_state.accumulatorOut().storedType()) + u')';
680 const QString initialization = u"aotContext->initLoadGlobalLookup("_s
681 + QString::number(index) + u')';
682 generateLookup(lookup, initialization);
683}
684
685void QQmlJSCodeGenerator::generate_LoadQmlContextPropertyLookup(int index)
686{
687 INJECT_TRACE_INFO(generate_LoadQmlContextPropertyLookup);
688
689 AccumulatorConverter registers(this);
690
691 const int nameIndex = m_jsUnitGenerator->lookupNameIndex(index);
692 const QString name = m_jsUnitGenerator->stringForIndex(index: nameIndex);
693 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptGlobal) {
694 m_body += m_state.accumulatorVariableOut + u" = "_s
695 + conversion(
696 from: m_typeResolver->original(type: m_state.accumulatorOut()), to: m_state.accumulatorOut(),
697 variable: u"aotContext->javaScriptGlobalProperty("_s + QString::number(nameIndex) + u")")
698 + u";\n"_s;
699 return;
700 }
701
702 const QString indexString = QString::number(index);
703 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectById) {
704 const QString lookup = u"aotContext->loadContextIdLookup("_s
705 + indexString + u", "_s
706 + contentPointer(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut) + u')';
707 const QString initialization = u"aotContext->initLoadContextIdLookup("_s
708 + indexString + u')';
709 generateLookup(lookup, initialization);
710 return;
711 }
712
713 const bool isProperty = m_state.accumulatorOut().isProperty();
714 const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
715 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
716 if (isProperty) {
717 const auto lookupType = contentType(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut);
718
719 const QString lookup = u"aotContext->loadScopeObjectPropertyLookup("_s
720 + indexString + u", "_s
721 + contentPointer(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut) + u')';
722 const QString initialization
723 = u"aotContext->initLoadScopeObjectPropertyLookup("_s
724 + indexString + u", "_s
725 + lookupType + u')';
726 const QString preparation = getLookupPreparation(
727 content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut, lookup: index);
728
729 generateLookup(lookup, initialization, resultPreparation: preparation);
730 } else if (m_state.accumulatorOut().isType() || m_state.accumulatorOut().isImportNamespace()) {
731 generateTypeLookup(index);
732 } else {
733 reject(thing: u"lookup of %1"_s.arg(a: m_state.accumulatorOut().descriptiveName()));
734 }
735}
736
737void QQmlJSCodeGenerator::generate_StoreNameSloppy(int nameIndex)
738{
739 INJECT_TRACE_INFO(generate_StoreNameSloppy);
740
741 const QString name = m_jsUnitGenerator->stringForIndex(index: nameIndex);
742 const QQmlJSRegisterContent type = m_typeResolver->scopedType(scope: m_function->qmlScope, name);
743 Q_ASSERT(type.isProperty());
744
745 switch (type.variant()) {
746 case QQmlJSRegisterContent::ScopeProperty:
747 case QQmlJSRegisterContent::ExtensionScopeProperty: {
748 // Do not convert here. We may intentionally pass the "wrong" type, for example to trigger
749 // a property reset.
750 m_body += u"aotContext->storeNameSloppy("_s + QString::number(nameIndex)
751 + u", "_s
752 + contentPointer(content: m_state.accumulatorIn(), var: m_state.accumulatorVariableIn)
753 + u", "_s
754 + contentType(content: m_state.accumulatorIn(), var: m_state.accumulatorVariableIn) + u')';
755 m_body += u";\n"_s;
756 break;
757 }
758 case QQmlJSRegisterContent::ScopeMethod:
759 case QQmlJSRegisterContent::ExtensionScopeMethod:
760 reject(thing: u"assignment to scope method"_s);
761 break;
762 default:
763 Q_UNREACHABLE();
764 }
765}
766
767void QQmlJSCodeGenerator::generate_StoreNameStrict(int name)
768{
769 Q_UNUSED(name)
770 BYTECODE_UNIMPLEMENTED();
771}
772
773void QQmlJSCodeGenerator::generate_LoadElement(int base)
774{
775 INJECT_TRACE_INFO(generate_LoadElement);
776
777 const QQmlJSRegisterContent baseType = registerType(index: base);
778
779 if (!baseType.isList()
780 && !m_typeResolver->registerIsStoredIn(reg: baseType, type: m_typeResolver->stringType())) {
781 reject(thing: u"LoadElement with non-list base type "_s + baseType.descriptiveName());
782 return;
783 }
784
785 const QString voidAssignment = u" "_s + m_state.accumulatorVariableOut + u" = "_s +
786 conversion(from: m_typeResolver->globalType(type: m_typeResolver->voidType()),
787 to: m_state.accumulatorOut(), variable: QString()) + u";\n"_s;
788
789 QString indexName = m_state.accumulatorVariableIn;
790 QQmlJSScope::ConstPtr indexType;
791 if (m_typeResolver->isNumeric(type: m_state.accumulatorIn())) {
792 indexType = m_typeResolver->containedType(container: m_state.accumulatorIn());
793 } else if (m_state.accumulatorIn().isConversion()) {
794 const auto target = m_typeResolver->extractNonVoidFromOptionalType(content: m_state.accumulatorIn());
795 if (m_typeResolver->isNumeric(type: target)) {
796 indexType = target;
797 m_body += u"if (!" + indexName + u".metaType().isValid())\n"
798 + voidAssignment
799 + u"else ";
800 indexName = convertStored(
801 from: m_state.accumulatorIn().storedType(), to: indexType, variable: indexName);
802 } else {
803 reject(thing: u"LoadElement with non-numeric argument"_s);
804 return;
805 }
806 }
807
808 AccumulatorConverter registers(this);
809 const QString baseName = registerVariable(index: base);
810
811 if (!m_typeResolver->isNativeArrayIndex(type: indexType)) {
812 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u"))\n"_s
813 + voidAssignment
814 + u"else "_s;
815 } else if (!m_typeResolver->isUnsignedInteger(type: indexType)) {
816 m_body += u"if ("_s + indexName + u" < 0)\n"_s
817 + voidAssignment
818 + u"else "_s;
819 }
820
821 if (m_typeResolver->registerIsStoredIn(reg: baseType, type: m_typeResolver->listPropertyType())) {
822 // Our QQmlListProperty only keeps plain QObject*.
823 const auto elementType = m_typeResolver->globalType(type: m_typeResolver->qObjectType());
824
825 m_body += u"if ("_s + indexName + u" < "_s + baseName
826 + u".count(&"_s + baseName + u"))\n"_s;
827 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
828 conversion(from: elementType, to: m_state.accumulatorOut(),
829 variable: baseName + u".at(&"_s + baseName + u", "_s
830 + indexName + u')') + u";\n"_s;
831 m_body += u"else\n"_s
832 + voidAssignment;
833 return;
834 }
835
836 const auto elementType = m_typeResolver->valueType(list: baseType);
837
838 QString access = baseName + u".at("_s + indexName + u')';
839
840 // TODO: Once we get a char type in QML, use it here.
841 if (m_typeResolver->registerIsStoredIn(reg: baseType, type: m_typeResolver->stringType()))
842 access = u"QString("_s + access + u")"_s;
843 else if (m_state.isRegisterAffectedBySideEffects(registerIndex: base))
844 reject(thing: u"LoadElement on a sequence potentially affected by side effects"_s);
845 else if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
846 reject(thing: u"LoadElement on a sequence wrapped in a non-sequence type"_s);
847
848 m_body += u"if ("_s + indexName + u" < "_s + baseName + u".size())\n"_s;
849 m_body += u" "_s + m_state.accumulatorVariableOut + u" = "_s +
850 conversion(from: elementType, to: m_state.accumulatorOut(), variable: access) + u";\n"_s;
851 m_body += u"else\n"_s
852 + voidAssignment;
853}
854
855void QQmlJSCodeGenerator::generate_StoreElement(int base, int index)
856{
857 INJECT_TRACE_INFO(generate_StoreElement);
858
859 const QQmlJSRegisterContent baseType = registerType(index: base);
860 const QQmlJSScope::ConstPtr indexType = m_typeResolver->containedType(container: registerType(index));
861
862 if (!m_typeResolver->isNumeric(type: indexType) || !baseType.isList()) {
863 reject(thing: u"StoreElement with non-list base type or non-numeric arguments"_s);
864 return;
865 }
866
867 if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
868 reject(thing: u"indirect StoreElement"_s);
869 return;
870 }
871
872 const QString baseName = registerVariable(index: base);
873 const QString indexName = registerVariable(index);
874
875 const auto valueType = m_typeResolver->valueType(list: baseType);
876 const auto elementType = m_typeResolver->globalType(type: m_typeResolver->genericType(
877 type: m_typeResolver->containedType(container: valueType)));
878
879 addInclude(include: u"QtQml/qjslist.h"_s);
880 if (!m_typeResolver->isNativeArrayIndex(type: indexType))
881 m_body += u"if (QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s;
882 else if (!m_typeResolver->isUnsignedInteger(type: indexType))
883 m_body += u"if ("_s + indexName + u" >= 0) {\n"_s;
884 else
885 m_body += u"{\n"_s;
886
887 if (m_typeResolver->registerIsStoredIn(reg: baseType, type: m_typeResolver->listPropertyType())) {
888 m_body += u" if ("_s + indexName + u" < "_s + baseName + u".count(&"_s + baseName
889 + u"))\n"_s;
890 m_body += u" "_s + baseName + u".replace(&"_s + baseName
891 + u", "_s + indexName + u", "_s;
892 m_body += conversion(from: m_state.accumulatorIn(), to: elementType, variable: m_state.accumulatorVariableIn)
893 + u");\n"_s;
894 m_body += u"}\n"_s;
895 return;
896 }
897
898 if (m_state.isRegisterAffectedBySideEffects(registerIndex: base))
899 reject(thing: u"LoadElement on a sequence potentially affected by side effects"_s);
900
901 m_body += u" if ("_s + indexName + u" >= " + baseName + u".size())\n"_s;
902 m_body += u" QJSList(&"_s + baseName + u", aotContext->engine).resize("_s
903 + indexName + u" + 1);\n"_s;
904 m_body += u" "_s + baseName + u'[' + indexName + u"] = "_s;
905 m_body += conversion(from: m_state.accumulatorIn(), to: elementType, variable: m_state.accumulatorVariableIn)
906 + u";\n"_s;
907 m_body += u"}\n"_s;
908
909 generateWriteBack(registerIndex: base);
910}
911
912void QQmlJSCodeGenerator::generate_LoadProperty(int nameIndex)
913{
914 Q_UNUSED(nameIndex)
915 reject(thing: u"LoadProperty"_s);
916}
917
918void QQmlJSCodeGenerator::generate_LoadOptionalProperty(int name, int offset)
919{
920 Q_UNUSED(name)
921 Q_UNUSED(offset)
922 BYTECODE_UNIMPLEMENTED();
923}
924
925void QQmlJSCodeGenerator::generateEnumLookup(int index)
926{
927 const QString enumMember = m_state.accumulatorOut().enumMember();
928
929 if (enumMember.isEmpty()) {
930 // If we're referring to the type, there's nothing to do.
931 // However, we should not get here since no one can ever use the enum metatype.
932 // The lookup is dead code and should be optimized away.
933 // ... unless you are actually trying to store the metatype itself in a property.
934 // We cannot compile such code.
935 reject(thing: u"Lookup of enum metatype"_s);
936 return;
937 }
938
939 // If the metaenum has the value, just use it and skip all the rest.
940 const QQmlJSMetaEnum metaEnum = m_state.accumulatorOut().enumeration();
941 if (metaEnum.hasValues()) {
942 m_body += m_state.accumulatorVariableOut + u" = "_s
943 + QString::number(metaEnum.value(key: enumMember));
944 m_body += u";\n"_s;
945 return;
946 }
947
948 const QQmlJSScope::ConstPtr scopeType = m_state.accumulatorOut().scopeType();
949
950 // Otherwise we would have found an enum with values.
951 Q_ASSERT(!scopeType->isComposite());
952
953 const QString enumName = metaEnum.isFlag() ? metaEnum.alias() : metaEnum.name();
954 if (enumName.isEmpty()) {
955 if (metaEnum.isFlag() && !metaEnum.name().isEmpty())
956 reject(thing: u"qmltypes misses name entry for flag; did you pass the enum type to Q_FLAG instead of the QFlag type?"
957 "\nType is %1, enum name is %2"_s.arg(args: scopeType->internalName(), args: metaEnum.name()));
958 reject(thing: u"qmltypes misses name entry for enum"_s);
959 }
960 const QString lookup = u"aotContext->getEnumLookup("_s + QString::number(index)
961 + u", &"_s + m_state.accumulatorVariableOut + u')';
962 const QString initialization = u"aotContext->initGetEnumLookup("_s
963 + QString::number(index) + u", "_s + metaObject(objectType: scopeType)
964 + u", \""_s + enumName + u"\", \""_s + enumMember
965 + u"\")"_s;
966 generateLookup(lookup, initialization);
967}
968
969void QQmlJSCodeGenerator::generateTypeLookup(int index)
970{
971 const QString indexString = QString::number(index);
972 const QQmlJSRegisterContent accumulatorIn = m_state.registers.value(key: Accumulator).content;
973 const QString namespaceString
974 = accumulatorIn.isImportNamespace()
975 ? QString::number(accumulatorIn.importNamespace())
976 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
977
978 switch (m_state.accumulatorOut().variant()) {
979 case QQmlJSRegisterContent::Singleton: {
980 rejectIfNonQObjectOut(error: u"non-QObject singleton type"_s);
981 const QString lookup = u"aotContext->loadSingletonLookup("_s + indexString
982 + u", &"_s + m_state.accumulatorVariableOut + u')';
983 const QString initialization = u"aotContext->initLoadSingletonLookup("_s + indexString
984 + u", "_s + namespaceString + u')';
985 generateLookup(lookup, initialization);
986 break;
987 }
988 case QQmlJSRegisterContent::ScopeModulePrefix:
989 break;
990 case QQmlJSRegisterContent::ScopeAttached: {
991 rejectIfNonQObjectOut(error: u"non-QObject attached type"_s);
992 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
993 + u", aotContext->qmlScopeObject, &"_s + m_state.accumulatorVariableOut + u')';
994 const QString initialization = u"aotContext->initLoadAttachedLookup("_s + indexString
995 + u", "_s + namespaceString + u", aotContext->qmlScopeObject)"_s;
996 generateLookup(lookup, initialization);
997 break;
998 }
999 case QQmlJSRegisterContent::Script:
1000 reject(thing: u"script lookup"_s);
1001 break;
1002 case QQmlJSRegisterContent::MetaType: {
1003 if (!m_typeResolver->registerIsStoredIn(
1004 reg: m_state.accumulatorOut(), type: m_typeResolver->metaObjectType())) {
1005 // TODO: Can we trigger this somehow?
1006 // It might be impossible, but we better be safe here.
1007 reject(thing: u"meta-object stored in different type"_s);
1008 }
1009 const QString lookup = u"aotContext->loadTypeLookup("_s + indexString
1010 + u", &"_s + m_state.accumulatorVariableOut + u')';
1011 const QString initialization = u"aotContext->initLoadTypeLookup("_s + indexString
1012 + u", "_s + namespaceString + u")"_s;
1013 generateLookup(lookup, initialization);
1014 break;
1015 }
1016 default:
1017 Q_UNREACHABLE();
1018 }
1019}
1020
1021void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1022 const QQmlJSRegisterContent &nonStorableContent, const QString &registerName, bool invert)
1023{
1024 const auto nonStorableType = m_typeResolver->containedType(container: nonStorableContent);
1025 QQmlJSScope::ConstPtr comparedType =
1026 m_typeResolver->equals(a: nonStorableType, b: m_typeResolver->nullType())
1027 ? m_typeResolver->nullType()
1028 : m_typeResolver->voidType();
1029
1030 // The common operations for both nulltype and voidtype
1031 m_body += u"if ("_s + registerName
1032 + u".metaType() == QMetaType::fromType<QJSPrimitiveValue>()) {\n"_s
1033 + m_state.accumulatorVariableOut + u" = "_s
1034 + conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
1035 variable: u"static_cast<const QJSPrimitiveValue *>("_s + registerName
1036 + u".constData())"_s + u"->type() "_s
1037 + (invert ? u"!="_s : u"=="_s)
1038 + (m_typeResolver->equals(a: comparedType, b: m_typeResolver->nullType())
1039 ? u"QJSPrimitiveValue::Null"_s
1040 : u"QJSPrimitiveValue::Undefined"_s))
1041 + u";\n} else if ("_s + registerName
1042 + u".metaType() == QMetaType::fromType<QJSValue>()) {\n"_s
1043 + m_state.accumulatorVariableOut + u" = "_s
1044 + conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
1045 variable: (invert ? u"!"_s : QString()) + u"static_cast<const QJSValue *>("_s
1046 + registerName + u".constData())"_s + u"->"_s
1047 + (m_typeResolver->equals(a: comparedType, b: m_typeResolver->nullType())
1048 ? u"isNull()"_s
1049 : u"isUndefined()"_s))
1050 + u";\n}"_s;
1051
1052 // Generate nullType specific operations (the case when variant contains QObject * or
1053 // std::nullptr_t)
1054 if (m_typeResolver->equals(a: nonStorableType, b: m_typeResolver->nullType())) {
1055 m_body += u"else if ("_s + registerName
1056 + u".metaType().flags().testFlag(QMetaType::PointerToQObject)) {\n"_s
1057 + m_state.accumulatorVariableOut + u" = "_s
1058 + conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
1059 variable: u"*static_cast<QObject *const *>("_s + registerName
1060 + u".constData())"_s + (invert ? u"!="_s : u"=="_s)
1061 + u" nullptr"_s)
1062 + u";\n} else if ("_s + registerName
1063 + u".metaType() == QMetaType::fromType<std::nullptr_t>()) {\n"_s
1064 + m_state.accumulatorVariableOut + u" = "_s
1065 + conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
1066 variable: (invert ? u"false"_s : u"true"_s))
1067 + u";\n}\n"_s;
1068 }
1069
1070 // fallback case (if variant contains a different type, then it is not null or undefined)
1071 m_body += u"else {\n"_s + m_state.accumulatorVariableOut + u" = "_s
1072 + conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
1073 variable: (invert ? (registerName + u".isValid() ? true : false"_s)
1074 : (registerName + u".isValid() ? false : true"_s)))
1075 + u";\n}"_s;
1076}
1077
1078void QQmlJSCodeGenerator::generateVariantEqualityComparison(
1079 const QQmlJSRegisterContent &storableContent, const QString &typedRegisterName,
1080 const QString &varRegisterName, bool invert)
1081{
1082 // enumerations are ===-equal to their underlying type and they are stored as such.
1083 // Therefore, use the underlying type right away.
1084 const auto contained = storableContent.isEnumeration()
1085 ? storableContent.storedType()
1086 : m_typeResolver->containedType(container: storableContent);
1087
1088 if (contained->isReferenceType()) {
1089 const QQmlJSRegisterContent comparable
1090 = m_typeResolver->builtinType(type: m_typeResolver->qObjectType());
1091 m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString()) + u"(("
1092 + varRegisterName + u".metaType().flags() & QMetaType::PointerToQObject) "_s
1093 + u" && "_s + conversion(from: storableContent, to: comparable, variable: typedRegisterName) + u" == "_s
1094 + conversion(from: m_typeResolver->varType(), to: comparable, variable: varRegisterName) + u");\n";
1095 return;
1096 }
1097
1098 if (m_typeResolver->isPrimitive(type: contained)) {
1099 const QQmlJSRegisterContent comparable
1100 = m_typeResolver->builtinType(type: m_typeResolver->jsPrimitiveType());
1101 m_body += m_state.accumulatorVariableOut + u" = "_s + (invert ? u"!"_s : QString())
1102 + conversion(from: storableContent, to: comparable, variable: typedRegisterName)
1103 + u".strictlyEquals("_s
1104 + conversion(from: m_typeResolver->varType(), to: comparable, variable: varRegisterName) + u");\n"_s;
1105 return;
1106 }
1107
1108 reject(thing: u"comparison of non-primitive, non-object type to var"_s);
1109}
1110
1111void QQmlJSCodeGenerator::generateArrayInitializer(int argc, int argv)
1112{
1113 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1114 const QQmlJSScope::ConstPtr value = stored->valueType();
1115 Q_ASSERT(value);
1116
1117 QStringList initializer;
1118 for (int i = 0; i < argc; ++i) {
1119 initializer += convertStored(
1120 from: registerType(index: argv + i).storedType(), to: value,
1121 variable: consumedRegisterVariable(index: argv + i));
1122 }
1123
1124 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->internalName() + u'{';
1125 m_body += initializer.join(sep: u", "_s);
1126 m_body += u"};\n";
1127}
1128
1129void QQmlJSCodeGenerator::generateWriteBack(int registerIndex)
1130{
1131 QString writeBackRegister = registerVariable(index: registerIndex);
1132 bool writeBackAffectedBySideEffects = m_state.isRegisterAffectedBySideEffects(registerIndex);
1133
1134 for (QQmlJSRegisterContent writeBack = registerType(index: registerIndex);
1135 !writeBack.storedType()->isReferenceType();) {
1136 if (writeBackAffectedBySideEffects)
1137 reject(thing: u"write-back of value affected by side effects"_s);
1138
1139 if (writeBack.isConversion())
1140 reject(thing: u"write-back of converted value"_s);
1141
1142 const int lookupIndex = writeBack.resultLookupIndex();
1143 if (lookupIndex == -1) {
1144 // This is essential for the soundness of the type system.
1145 //
1146 // If a value or a list is returned from a function, we cannot know
1147 // whether it is a copy or a reference. Therefore, we cannot know whether
1148 // we have to write it back and so we have to reject any write on it.
1149 //
1150 // Only if we are sure that the value is locally created we can be sure
1151 // we don't have to write it back. In this latter case we could allow
1152 // a modification that doesn't write back.
1153 reject(thing: u"write-back of non-lookup"_s);
1154 break;
1155 }
1156
1157 const QString writeBackIndexString = QString::number(lookupIndex);
1158
1159 const QQmlJSRegisterContent::ContentVariant variant = writeBack.variant();
1160 if (variant == QQmlJSRegisterContent::ScopeProperty
1161 || variant == QQmlJSRegisterContent::ExtensionScopeProperty) {
1162 const QString lookup = u"aotContext->writeBackScopeObjectPropertyLookup("_s
1163 + writeBackIndexString
1164 + u", "_s + contentPointer(content: writeBack, var: writeBackRegister) + u')';
1165 const QString initialization
1166 = u"aotContext->initLoadScopeObjectPropertyLookup("_s
1167 + writeBackIndexString
1168 + u", "_s + contentType(content: writeBack, var: writeBackRegister) + u')';
1169 generateLookup(lookup, initialization);
1170 break;
1171 }
1172
1173
1174 QQmlJSRegisterContent outerContent;
1175 QString outerRegister;
1176 bool outerAffectedBySideEffects = false;
1177 for (auto it = m_state.lookups.constBegin(), end = m_state.lookups.constEnd();
1178 it != end; ++it) {
1179 if (it.value().content.resultLookupIndex() == writeBack.baseLookupIndex()) {
1180 outerContent = it.value().content;
1181 outerRegister = lookupVariable(lookupIndex: outerContent.resultLookupIndex());
1182 outerAffectedBySideEffects = it.value().affectedBySideEffects;
1183 break;
1184 }
1185 }
1186
1187 if (!outerContent.isValid()) {
1188 // If the lookup doesn't exist, it was killed by state merge.
1189 reject(thing: u"write-back of lookup across jumps or merges."_s);
1190 break;
1191 }
1192
1193 Q_ASSERT(!outerRegister.isEmpty());
1194
1195 switch (writeBack.variant()) {
1196 case QQmlJSRegisterContent::ScopeProperty:
1197 case QQmlJSRegisterContent::ExtensionScopeProperty:
1198 Q_UNREACHABLE();
1199 case QQmlJSRegisterContent::ObjectProperty:
1200 case QQmlJSRegisterContent::ExtensionObjectProperty:
1201 if (writeBack.scopeType()->isReferenceType()) {
1202 const QString lookup = u"aotContext->writeBackObjectLookup("_s
1203 + writeBackIndexString
1204 + u", "_s + outerRegister
1205 + u", "_s + contentPointer(content: writeBack, var: writeBackRegister) + u')';
1206 const QString initialization = u"aotContext->initGetObjectLookup("_s
1207 + writeBackIndexString
1208 + u", "_s + outerRegister
1209 + u", "_s + contentType(content: writeBack, var: writeBackRegister) + u')';
1210 generateLookup(lookup, initialization);
1211 } else {
1212 const QString valuePointer = contentPointer(content: outerContent, var: outerRegister);
1213 const QString lookup = u"aotContext->writeBackValueLookup("_s
1214 + writeBackIndexString
1215 + u", "_s + valuePointer
1216 + u", "_s + contentPointer(content: writeBack, var: writeBackRegister) + u')';
1217 const QString initialization = u"aotContext->initGetValueLookup("_s
1218 + writeBackIndexString
1219 + u", "_s + metaObject(objectType: writeBack.scopeType())
1220 + u", "_s + contentType(content: writeBack, var: writeBackRegister) + u')';
1221 generateLookup(lookup, initialization);
1222 }
1223 break;
1224 default:
1225 reject(thing: u"SetLookup on value types (because of missing write-back)"_s);
1226 }
1227
1228 writeBackRegister = outerRegister;
1229 writeBack = outerContent;
1230 writeBackAffectedBySideEffects = outerAffectedBySideEffects;
1231 }
1232}
1233
1234void QQmlJSCodeGenerator::rejectIfNonQObjectOut(const QString &error)
1235{
1236 if (m_state.accumulatorOut().storedType()->accessSemantics()
1237 != QQmlJSScope::AccessSemantics::Reference) {
1238 reject(thing: error);
1239 }
1240}
1241
1242void QQmlJSCodeGenerator::rejectIfBadArray()
1243{
1244 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
1245 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence) {
1246 // This rejects any attempt to store the list into a QVariant.
1247 // Therefore, we don't have to adjust the contained type below.
1248
1249 reject(thing: u"storing an array in a non-sequence type"_s);
1250 } else if (stored->isListProperty()) {
1251 // We can, technically, generate code for this. But it's dangerous:
1252 //
1253 // const QString storage = m_state.accumulatorVariableOut + u"_storage"_s;
1254 // m_body += stored->internalName() + u"::ListType " + storage
1255 // + u" = {"_s + initializer.join(u", "_s) + u"};\n"_s;
1256 // m_body += m_state.accumulatorVariableOut
1257 // + u" = " + stored->internalName() + u"(nullptr, &"_s + storage + u");\n"_s;
1258
1259 reject(thing: u"creating a QQmlListProperty not backed by a property"_s);
1260
1261 }
1262}
1263
1264/*!
1265 * \internal
1266 *
1267 * generates a check for the content pointer to be valid.
1268 * Returns true if the content pointer needs to be retrieved from a QVariant, or
1269 * false if the variable can be used as-is.
1270 */
1271bool QQmlJSCodeGenerator::generateContentPointerCheck(
1272 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1273 const QString &variable, const QString &errorMessage)
1274{
1275 const QQmlJSScope::ConstPtr scope = required;
1276 const QQmlJSScope::ConstPtr input = m_typeResolver->containedType(container: actual);
1277 if (QQmlJSUtils::searchBaseAndExtensionTypes(type: input,
1278 check: [&](const QQmlJSScope::ConstPtr &base) {
1279 return m_typeResolver->equals(a: base, b: scope);
1280 })) {
1281 return false;
1282 }
1283
1284 if (!m_typeResolver->canHold(container: input, contained: scope)) {
1285 reject(thing: u"lookup of members of %1 in %2"_s.arg(
1286 args: scope->internalName(), args: input->internalName()));
1287 }
1288
1289 bool needsVarContentConversion = false;
1290 QString processedErrorMessage;
1291 if (actual.storedType()->isReferenceType()) {
1292 // Since we have verified the type in qqmljstypepropagator.cpp we now know
1293 // that we can only have either null or the actual type here. Therefore,
1294 // it's enough to check the pointer for null.
1295 m_body += u"if ("_s + variable + u" == nullptr) {\n "_s;
1296 processedErrorMessage = errorMessage.arg(a: u"null");
1297 } else if (m_typeResolver->equals(a: actual.storedType(), b: m_typeResolver->varType())) {
1298 // Since we have verified the type in qqmljstypepropagator.cpp we now know
1299 // that we can only have either undefined or the actual type here. Therefore,
1300 // it's enough to check the QVariant for isValid().
1301 m_body += u"if (!"_s + variable + u".isValid()) {\n "_s;
1302 needsVarContentConversion = true;
1303 processedErrorMessage = errorMessage.arg(a: u"undefined");
1304 } else {
1305 reject(thing: u"retrieving metatype from %1"_s.arg(a: actual.descriptiveName()));
1306 }
1307
1308 generateSetInstructionPointer();
1309 m_body += u" aotContext->engine->throwError(QJSValue::TypeError, "_s;
1310 m_body += u"QLatin1String(\"%1\"));\n"_s.arg(a: processedErrorMessage);
1311 generateReturnError();
1312 m_body += u"}\n"_s;
1313 return needsVarContentConversion;
1314}
1315
1316QString QQmlJSCodeGenerator::resolveValueTypeContentPointer(
1317 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1318 const QString &variable, const QString &errorMessage)
1319{
1320 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1321 return variable + u".data()"_s;
1322 return contentPointer(content: actual, var: variable);
1323}
1324
1325QString QQmlJSCodeGenerator::resolveQObjectPointer(
1326 const QQmlJSScope::ConstPtr &required, const QQmlJSRegisterContent &actual,
1327 const QString &variable, const QString &errorMessage)
1328{
1329 if (generateContentPointerCheck(required, actual, variable, errorMessage))
1330 return u"*static_cast<QObject *const *>("_s + variable + u".constData())"_s;
1331 return variable;
1332}
1333
1334void QQmlJSCodeGenerator::generate_GetLookup(int index)
1335{
1336 INJECT_TRACE_INFO(generate_GetLookup);
1337 generate_GetLookupHelper(index);
1338}
1339
1340void QQmlJSCodeGenerator::generate_GetLookupHelper(int index)
1341{
1342 if (m_state.accumulatorOut().isMethod()) {
1343 reject(thing: u"lookup of function property."_s);
1344 return;
1345 }
1346
1347 if (m_typeResolver->equals(a: m_state.accumulatorOut().scopeType(), b: m_typeResolver->mathObject())) {
1348 QString name = m_jsUnitGenerator->lookupName(index);
1349
1350 double value{};
1351 if (name == u"E") {
1352 value = std::exp(x: 1.0);
1353 } else if (name == u"LN10") {
1354 value = log(x: 10.0);
1355 } else if (name == u"LN2") {
1356 value = log(x: 2.0);
1357 } else if (name == u"LOG10E") {
1358 value = log10(x: std::exp(x: 1.0));
1359 } else if (name == u"LOG2E") {
1360 value = log2(x: std::exp(x: 1.0));
1361 } else if (name == u"PI") {
1362 value = 3.14159265358979323846;
1363 } else if (name == u"SQRT1_2") {
1364 value = std::sqrt(x: 0.5);
1365 } else if (name == u"SQRT2") {
1366 value = std::sqrt(x: 2.0);
1367 } else {
1368 Q_UNREACHABLE();
1369 }
1370
1371 m_body += m_state.accumulatorVariableOut + u" = "_s
1372 + conversion(from: m_typeResolver->realType(), to: m_state.accumulatorOut(), variable: toNumericString(value))
1373 + u";\n"_s;
1374 return;
1375 }
1376
1377 if (m_state.accumulatorOut().isImportNamespace()) {
1378 Q_ASSERT(m_state.accumulatorOut().variant() == QQmlJSRegisterContent::ObjectModulePrefix);
1379 // If we have an object module prefix, we need to pass through the original object.
1380 if (m_state.accumulatorVariableIn != m_state.accumulatorVariableOut) {
1381 m_body += m_state.accumulatorVariableOut + u" = "_s
1382 + conversion(from: m_state.accumulatorIn(), to: m_state.accumulatorOut(),
1383 variable: m_state.accumulatorVariableIn)
1384 + u";\n"_s;
1385 }
1386 return;
1387 }
1388
1389 AccumulatorConverter registers(this);
1390
1391 if (m_state.accumulatorOut().isEnumeration()) {
1392 generateEnumLookup(index);
1393 return;
1394 }
1395
1396 const QString indexString = QString::number(index);
1397 const QString namespaceString = m_state.accumulatorIn().isImportNamespace()
1398 ? QString::number(m_state.accumulatorIn().importNamespace())
1399 : u"QQmlPrivate::AOTCompiledContext::InvalidStringId"_s;
1400 const auto accumulatorIn = m_state.accumulatorIn();
1401 const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
1402 const bool isReferenceType = scope->isReferenceType();
1403
1404 switch (m_state.accumulatorOut().variant()) {
1405 case QQmlJSRegisterContent::ObjectAttached: {
1406 if (!isReferenceType) {
1407 // This can happen on incomplete type information. We contextually know that the
1408 // type must be a QObject, but we cannot construct the inheritance chain. Then we
1409 // store it in a generic type. Technically we could even convert it to QObject*, but
1410 // that would be expensive.
1411 reject(thing: u"attached object for non-QObject type"_s);
1412 }
1413
1414 if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
1415 // This can happen if we retroactively determine that the property might not be
1416 // what we think it is (ie, it can be shadowed).
1417 reject(thing: u"attached object of potentially non-QObject base"_s);
1418 }
1419
1420 rejectIfNonQObjectOut(error: u"non-QObject attached type"_s);
1421
1422 const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString
1423 + u", "_s + m_state.accumulatorVariableIn
1424 + u", &"_s + m_state.accumulatorVariableOut + u')';
1425 const QString initialization = u"aotContext->initLoadAttachedLookup("_s
1426 + indexString + u", "_s + namespaceString + u", "_s
1427 + m_state.accumulatorVariableIn + u')';
1428 generateLookup(lookup, initialization);
1429 return;
1430 }
1431 case QQmlJSRegisterContent::ScopeAttached:
1432 case QQmlJSRegisterContent::Singleton:
1433 case QQmlJSRegisterContent::Script:
1434 case QQmlJSRegisterContent::MetaType: {
1435 generateTypeLookup(index);
1436 return;
1437 }
1438 default:
1439 break;
1440 }
1441
1442 Q_ASSERT(m_state.accumulatorOut().isProperty());
1443
1444 if (m_typeResolver->registerIsStoredIn(reg: accumulatorIn, type: m_typeResolver->jsValueType())) {
1445 reject(thing: u"lookup in QJSValue"_s);
1446 } else if (isReferenceType) {
1447 const QString inputPointer = resolveQObjectPointer(
1448 required: scope, actual: accumulatorIn, variable: m_state.accumulatorVariableIn,
1449 errorMessage: u"Cannot read property '%1' of %2"_s.arg(
1450 a: m_jsUnitGenerator->lookupName(index)));
1451 const QString lookup = u"aotContext->getObjectLookup("_s + indexString
1452 + u", "_s + inputPointer + u", "_s
1453 + contentPointer(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut) + u')';
1454 const QString initialization = u"aotContext->initGetObjectLookup("_s
1455 + indexString + u", "_s + inputPointer
1456 + u", "_s + contentType(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut)
1457 + u')';
1458 const QString preparation = getLookupPreparation(
1459 content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut, lookup: index);
1460 generateLookup(lookup, initialization, resultPreparation: preparation);
1461 } else if ((scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1462 || m_typeResolver->equals(a: scope, b: m_typeResolver->stringType()))
1463 && m_jsUnitGenerator->lookupName(index) == u"length"_s) {
1464 const QQmlJSScope::ConstPtr stored = accumulatorIn.storedType();
1465 if (stored->isListProperty()) {
1466 m_body += m_state.accumulatorVariableOut + u" = "_s;
1467 m_body += conversion(
1468 from: m_typeResolver->globalType(type: m_typeResolver->sizeType()),
1469 to: m_state.accumulatorOut(),
1470 variable: m_state.accumulatorVariableIn + u".count("_s + u'&'
1471 + m_state.accumulatorVariableIn + u')');
1472 m_body += u";\n"_s;
1473 } else if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1474 || m_typeResolver->equals(a: stored, b: m_typeResolver->stringType())) {
1475 m_body += m_state.accumulatorVariableOut + u" = "_s
1476 + conversion(from: m_typeResolver->globalType(type: m_typeResolver->sizeType()),
1477 to: m_state.accumulatorOut(),
1478 variable: m_state.accumulatorVariableIn + u".length()"_s)
1479 + u";\n"_s;
1480 } else {
1481 reject(thing: u"access to 'length' property of sequence wrapped in non-sequence"_s);
1482 }
1483 } else if (m_typeResolver->registerIsStoredIn(reg: accumulatorIn,
1484 type: m_typeResolver->variantMapType())) {
1485 QString mapLookup = m_state.accumulatorVariableIn + u"["_s
1486 + QQmlJSUtils::toLiteral(s: m_jsUnitGenerator->lookupName(index)) + u"]"_s;
1487 m_body += m_state.accumulatorVariableOut + u" = "_s;
1488 m_body += conversion(from: m_typeResolver->globalType(type: m_typeResolver->varType()),
1489 to: m_state.accumulatorOut(), variable: mapLookup);
1490 m_body += u";\n"_s;
1491 } else {
1492 if (m_state.isRegisterAffectedBySideEffects(registerIndex: Accumulator))
1493 reject(thing: u"reading from a value that's potentially affected by side effects"_s);
1494
1495 const QString inputContentPointer = resolveValueTypeContentPointer(
1496 required: scope, actual: accumulatorIn, variable: m_state.accumulatorVariableIn,
1497 errorMessage: u"Cannot read property '%1' of %2"_s.arg(
1498 a: m_jsUnitGenerator->lookupName(index)));
1499
1500 const QString lookup = u"aotContext->getValueLookup("_s + indexString
1501 + u", "_s + inputContentPointer
1502 + u", "_s + contentPointer(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut)
1503 + u')';
1504 const QString initialization = u"aotContext->initGetValueLookup("_s
1505 + indexString + u", "_s
1506 + metaObject(objectType: scope) + u", "_s
1507 + contentType(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut) + u')';
1508 const QString preparation = getLookupPreparation(
1509 content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut, lookup: index);
1510 generateLookup(lookup, initialization, resultPreparation: preparation);
1511 }
1512}
1513
1514void QQmlJSCodeGenerator::generate_GetOptionalLookup(int index, int offset)
1515{
1516 INJECT_TRACE_INFO(generate_GetOptionalLookup);
1517
1518 const QQmlJSRegisterContent accumulatorIn = m_state.accumulatorIn();
1519 QString accumulatorVarIn = m_state.accumulatorVariableIn;
1520
1521 const auto &annotation = m_annotations[currentInstructionOffset()];
1522 if (accumulatorIn.storedType()->isReferenceType()) {
1523 m_body += u"if (!%1)\n"_s.arg(a: accumulatorVarIn);
1524 generateJumpCodeWithTypeConversions(relativeOffset: offset);
1525 } else if (m_typeResolver->equals(a: accumulatorIn.storedType(), b: m_typeResolver->varType())) {
1526 m_body += u"if (!%1.isValid() || ((%1.metaType().flags() & QMetaType::PointerToQObject) "
1527 "&& %1.value<QObject *>() == nullptr))\n"_s.arg(a: accumulatorVarIn);
1528 generateJumpCodeWithTypeConversions(relativeOffset: offset);
1529 } else if (m_typeResolver->equals(a: accumulatorIn.storedType(), b: m_typeResolver->jsPrimitiveType())) {
1530 m_body += u"if (%1.equals(QJSPrimitiveUndefined()) "
1531 "|| %1.equals(QJSPrimitiveNull()))\n"_s.arg(a: accumulatorVarIn);
1532 generateJumpCodeWithTypeConversions(relativeOffset: offset);
1533 } else if (annotation.changedRegisterIndex == Accumulator
1534 && annotation.changedRegister.variant() == QQmlJSRegisterContent::ObjectEnum) {
1535 // Nothing
1536 } else if (m_typeResolver->equals(a: accumulatorIn.storedType(), b: m_typeResolver->jsValueType())) {
1537 m_body += u"if (%1.isNull() || %1.isUndefined())\n"_s.arg(a: accumulatorVarIn);
1538 generateJumpCodeWithTypeConversions(relativeOffset: offset);
1539 } else {
1540 Q_UNREACHABLE(); // No other accumulatorIn stored types should be possible
1541 }
1542
1543 generate_GetLookupHelper(index);
1544}
1545
1546void QQmlJSCodeGenerator::generate_StoreProperty(int nameIndex, int baseReg)
1547{
1548 Q_UNUSED(nameIndex)
1549 Q_UNUSED(baseReg)
1550 reject(thing: u"StoreProperty"_s);
1551}
1552
1553QString QQmlJSCodeGenerator::setLookupPreparation(
1554 const QQmlJSRegisterContent &content, const QString &arg, int lookup)
1555{
1556 if (m_typeResolver->registerContains(reg: content, type: content.storedType()))
1557 return QString();
1558
1559 if (m_typeResolver->registerIsStoredIn(reg: content, type: m_typeResolver->varType())) {
1560 return u"const QMetaType argType = aotContext->lookupResultMetaType("_s
1561 + QString::number(lookup) + u");\n"_s
1562 + u"if (argType.isValid())\n "_s + arg + u".convert(argType)";
1563 }
1564 // TODO: We could make sure they're compatible, for example QObject pointers.
1565 return QString();
1566}
1567
1568
1569void QQmlJSCodeGenerator::generate_SetLookup(int index, int baseReg)
1570{
1571 INJECT_TRACE_INFO(generate_SetLookup);
1572
1573 const QString indexString = QString::number(index);
1574 const QQmlJSScope::ConstPtr valueType = m_state.accumulatorIn().storedType();
1575 const QQmlJSRegisterContent specific = m_state.readAccumulator();
1576 Q_ASSERT(specific.isConversion());
1577 const QQmlJSScope::ConstPtr originalScope
1578 = m_typeResolver->originalType(type: specific.conversionResultScope());
1579
1580 if (specific.storedType().isNull()) {
1581 reject(thing: u"SetLookup. Could not find property "
1582 + m_jsUnitGenerator->lookupName(index)
1583 + u" on type "
1584 + originalScope->internalName());
1585 return;
1586 }
1587
1588 // Choose a container that can hold both, the "in" accumulator and what we actually want.
1589 // If the types are all the same because we can all store them as verbatim C++ types,
1590 // the container will also be that type.
1591
1592 QQmlJSRegisterContent property = specific;
1593 if (!m_typeResolver->equals(a: specific.storedType(), b: valueType)) {
1594 if (m_typeResolver->isPrimitive(type: specific.storedType())
1595 && m_typeResolver->isPrimitive(type: valueType)) {
1596 // Preferably store in QJSPrimitiveValue since we need the content pointer below.
1597 property = property.storedIn(newStoredType: m_typeResolver->jsPrimitiveType());
1598 } else {
1599 property = property.storedIn(newStoredType: m_typeResolver->merge(a: specific.storedType(), b: valueType));
1600 }
1601 }
1602
1603 const QString object = registerVariable(index: baseReg);
1604 m_body += u"{\n"_s;
1605 QString variableIn;
1606 QString variableInType;
1607 QString preparation;
1608 QString argType;
1609 if (!m_typeResolver->registerContains(
1610 reg: m_state.accumulatorIn(), type: m_typeResolver->containedType(container: property))) {
1611 m_body += u"auto converted = "_s
1612 + conversion(from: m_state.accumulatorIn(), to: property, variable: consumedAccumulatorVariableIn())
1613 + u";\n"_s;
1614 variableIn = contentPointer(content: property, var: u"converted"_s);
1615 variableInType = contentType(content: property, var: u"converted"_s);
1616 preparation = setLookupPreparation(content: property, arg: u"converted"_s, lookup: index);
1617 if (preparation.isEmpty())
1618 argType = contentType(content: property, var: u"converted"_s);
1619 else
1620 argType = u"argType"_s;
1621 } else {
1622 variableIn = contentPointer(content: property, var: m_state.accumulatorVariableIn);
1623 variableInType = contentType(content: property, var: m_state.accumulatorVariableIn);
1624 argType = variableInType;
1625 }
1626
1627 switch (originalScope->accessSemantics()) {
1628 case QQmlJSScope::AccessSemantics::Reference: {
1629 const QString basePointer = resolveQObjectPointer(
1630 required: originalScope, actual: registerType(index: baseReg), variable: object,
1631 errorMessage: u"TypeError: Value is %1 and could not be converted to an object"_s);
1632
1633 const QString lookup = u"aotContext->setObjectLookup("_s + indexString
1634 + u", "_s + basePointer + u", "_s + variableIn + u')';
1635 const QString initialization = u"aotContext->initSetObjectLookup("_s
1636 + indexString + u", "_s + basePointer + u", "_s + argType + u')';
1637 generateLookup(lookup, initialization, resultPreparation: preparation);
1638 break;
1639 }
1640 case QQmlJSScope::AccessSemantics::Sequence: {
1641 const QString propertyName = m_jsUnitGenerator->lookupName(index);
1642 if (propertyName != u"length"_s) {
1643 reject(thing: u"setting non-length property on a sequence type"_s);
1644 break;
1645 }
1646
1647 if (!originalScope->isListProperty()) {
1648 reject(thing: u"resizing sequence types (because of missing write-back)"_s);
1649 break;
1650 }
1651
1652 // We can resize without write back on a list property because it's actually a reference.
1653 m_body += u"const int begin = "_s + object + u".count(&" + object + u");\n"_s;
1654 m_body += u"const int end = "_s
1655 + (variableIn.startsWith(c: u'&') ? variableIn.mid(position: 1) : (u'*' + variableIn))
1656 + u";\n"_s;
1657 m_body += u"for (int i = begin; i < end; ++i)\n"_s;
1658 m_body += u" "_s + object + u".append(&"_s + object + u", nullptr);\n"_s;
1659 m_body += u"for (int i = begin; i > end; --i)\n"_s;
1660 m_body += u" "_s + object + u".removeLast(&"_s + object + u')'
1661 + u";\n"_s;
1662 break;
1663 }
1664 case QQmlJSScope::AccessSemantics::Value: {
1665 const QQmlJSRegisterContent base = registerType(index: baseReg);
1666 const QString baseContentPointer = resolveValueTypeContentPointer(
1667 required: originalScope, actual: base, variable: object,
1668 errorMessage: u"TypeError: Value is %1 and could not be converted to an object"_s);
1669
1670 const QString lookup = u"aotContext->setValueLookup("_s + indexString
1671 + u", "_s + baseContentPointer
1672 + u", "_s + variableIn + u')';
1673 const QString initialization = u"aotContext->initSetValueLookup("_s
1674 + indexString + u", "_s + metaObject(objectType: originalScope)
1675 + u", "_s + argType + u')';
1676
1677 generateLookup(lookup, initialization, resultPreparation: preparation);
1678 generateWriteBack(registerIndex: baseReg);
1679
1680 break;
1681 }
1682 case QQmlJSScope::AccessSemantics::None:
1683 Q_UNREACHABLE();
1684 break;
1685 }
1686
1687 m_body += u"}\n"_s;
1688}
1689
1690void QQmlJSCodeGenerator::generate_LoadSuperProperty(int property)
1691{
1692 Q_UNUSED(property)
1693 BYTECODE_UNIMPLEMENTED();
1694}
1695
1696void QQmlJSCodeGenerator::generate_StoreSuperProperty(int property)
1697{
1698 Q_UNUSED(property)
1699 BYTECODE_UNIMPLEMENTED();
1700}
1701
1702void QQmlJSCodeGenerator::generate_Yield()
1703{
1704 BYTECODE_UNIMPLEMENTED();
1705}
1706
1707void QQmlJSCodeGenerator::generate_YieldStar()
1708{
1709 BYTECODE_UNIMPLEMENTED();
1710}
1711
1712void QQmlJSCodeGenerator::generate_Resume(int)
1713{
1714 BYTECODE_UNIMPLEMENTED();
1715}
1716
1717QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
1718{
1719 QString types;
1720 QString args;
1721
1722 if (m_state.changedRegisterIndex() == InvalidRegister ||
1723 m_typeResolver->registerContains(
1724 reg: m_state.accumulatorOut(), type: m_typeResolver->voidType())) {
1725 types = u"QMetaType()"_s;
1726 args = u"nullptr"_s;
1727 } else {
1728 *outVar = u"callResult"_s;
1729 const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
1730 m_body += outType->augmentedInternalName() + u' ' + *outVar;
1731 if (!m_typeResolver->registerContains(reg: m_state.accumulatorOut(), type: outType)) {
1732 if (m_typeResolver->equals(a: outType, b: m_typeResolver->varType())
1733 || m_typeResolver->equals(a: outType, b: m_typeResolver->jsPrimitiveType())) {
1734 m_body += u'('
1735 + metaType(type: m_typeResolver->containedType(container: m_state.accumulatorOut()))
1736 + u')';
1737 }
1738 }
1739 m_body += u";\n";
1740
1741 args = contentPointer(content: m_state.accumulatorOut(), var: *outVar);
1742 types = contentType(content: m_state.accumulatorOut(), var: *outVar);
1743 }
1744
1745 for (int i = 0; i < argc; ++i) {
1746 const QQmlJSRegisterContent content = registerType(index: argv + i);
1747 const QString var = registerVariable(index: argv + i);
1748 args += u", "_s + contentPointer(content, var);
1749 types += u", "_s + contentType(content, var);
1750 }
1751
1752 return u"void *args[] = { "_s + args + u" };\n"_s
1753 + u"const QMetaType types[] = { "_s + types + u" };\n"_s;
1754}
1755
1756void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
1757{
1758 if (m_state.accumulatorVariableOut.isEmpty() || outVar.isEmpty())
1759 return;
1760
1761 m_body += m_state.accumulatorVariableOut + u" = "_s;
1762 m_body += u"std::move(" + outVar + u");\n";
1763}
1764
1765void QQmlJSCodeGenerator::generate_CallValue(int name, int argc, int argv)
1766{
1767 Q_UNUSED(name)
1768 Q_UNUSED(argc)
1769 Q_UNUSED(argv)
1770 BYTECODE_UNIMPLEMENTED();
1771}
1772
1773void QQmlJSCodeGenerator::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
1774{
1775 Q_UNUSED(name)
1776 Q_UNUSED(thisObject)
1777 Q_UNUSED(argc)
1778 Q_UNUSED(argv)
1779 BYTECODE_UNIMPLEMENTED();
1780}
1781
1782void QQmlJSCodeGenerator::generate_CallProperty(int nameIndex, int baseReg, int argc, int argv)
1783{
1784 Q_UNUSED(nameIndex);
1785 Q_UNUSED(baseReg);
1786 Q_UNUSED(argc);
1787 Q_UNUSED(argv);
1788 reject(thing: u"CallProperty"_s);
1789}
1790
1791bool QQmlJSCodeGenerator::inlineStringMethod(const QString &name, int base, int argc, int argv)
1792{
1793 if (name != u"arg"_s || argc != 1)
1794 return false;
1795
1796 const auto arg = [&](const QQmlJSScope::ConstPtr &type) {
1797 return convertStored(from: registerType(index: argv).storedType(), to: type, variable: consumedRegisterVariable(index: argv));
1798 };
1799
1800 const auto ret = [&](const QString &arg) {
1801 const QString expression = convertStored(
1802 from: registerType(index: base).storedType(), to: m_typeResolver->stringType(),
1803 variable: consumedRegisterVariable(index: base)) + u".arg("_s + arg + u')';
1804 return conversion(
1805 from: m_typeResolver->stringType(), to: m_state.accumulatorOut(), variable: expression);
1806 };
1807
1808 const QQmlJSRegisterContent input = m_state.readRegister(registerIndex: argv);
1809 m_body += m_state.accumulatorVariableOut + u" = "_s;
1810
1811 if (m_typeResolver->isNumeric(type: input))
1812 m_body += ret(arg(m_typeResolver->containedType(container: input)));
1813 else if (m_typeResolver->registerContains(reg: input, type: m_typeResolver->boolType()))
1814 m_body += ret(arg(m_typeResolver->boolType()));
1815 else
1816 m_body += ret(arg(m_typeResolver->stringType()));
1817 m_body += u";\n"_s;
1818 return true;
1819}
1820
1821bool QQmlJSCodeGenerator::inlineTranslateMethod(const QString &name, int argc, int argv)
1822{
1823 addInclude(include: u"QtCore/qcoreapplication.h"_s);
1824
1825 const auto arg = [&](int i, const QQmlJSScope::ConstPtr &type) {
1826 Q_ASSERT(i < argc);
1827 return convertStored(from: registerType(index: argv + i).storedType(), to: type,
1828 variable: consumedRegisterVariable(index: argv + i));
1829 };
1830
1831 const auto stringArg = [&](int i) {
1832 return i < argc
1833 ? (arg(i, m_typeResolver->stringType()) + u".toUtf8().constData()"_s)
1834 : u"\"\""_s;
1835 };
1836
1837 const auto intArg = [&](int i) {
1838 return i < argc ? arg(i, m_typeResolver->int32Type()) : u"-1"_s;
1839 };
1840
1841 const auto stringRet = [&](const QString &expression) {
1842 return conversion(
1843 from: m_typeResolver->stringType(), to: m_state.accumulatorOut(), variable: expression);
1844 };
1845
1846 const auto capture = [&]() {
1847 m_body += u"aotContext->captureTranslation();\n"_s;
1848 };
1849
1850 if (name == u"QT_TRID_NOOP"_s || name == u"QT_TR_NOOP"_s) {
1851 Q_ASSERT(argc > 0);
1852 m_body += m_state.accumulatorVariableOut + u" = "_s
1853 + stringRet(arg(0, m_typeResolver->stringType())) + u";\n"_s;
1854 return true;
1855 }
1856
1857 if (name == u"QT_TRANSLATE_NOOP"_s) {
1858 Q_ASSERT(argc > 1);
1859 m_body += m_state.accumulatorVariableOut + u" = "_s
1860 + stringRet(arg(1, m_typeResolver->stringType())) + u";\n"_s;
1861 return true;
1862 }
1863
1864 if (name == u"qsTrId"_s) {
1865 capture();
1866 // We inline qtTrId() here because in the !QT_CONFIG(translation) case it's unavailable.
1867 // QCoreApplication::translate() is always available in some primitive form.
1868 // Also, this saves a function call.
1869 m_body += m_state.accumulatorVariableOut + u" = "_s
1870 + stringRet(u"QCoreApplication::translate(nullptr, "_s + stringArg(0) +
1871 u", nullptr, "_s + intArg(1) + u")"_s) + u";\n"_s;
1872 return true;
1873 }
1874
1875 if (name == u"qsTr"_s) {
1876 capture();
1877 m_body += m_state.accumulatorVariableOut + u" = "_s
1878 + stringRet(u"QCoreApplication::translate("_s
1879 + u"aotContext->translationContext().toUtf8().constData(), "_s
1880 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1881 + intArg(2) + u")"_s) + u";\n"_s;
1882 return true;
1883 }
1884
1885 if (name == u"qsTranslate"_s) {
1886 capture();
1887 m_body += m_state.accumulatorVariableOut + u" = "_s
1888 + stringRet(u"QCoreApplication::translate("_s
1889 + stringArg(0) + u", "_s + stringArg(1) + u", "_s
1890 + stringArg(2) + u", "_s + intArg(3) + u")"_s) + u";\n"_s;
1891 return true;
1892 }
1893
1894 return false;
1895}
1896
1897static QString maxExpression(int argc)
1898{
1899 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "max() expects at least two arguments.");
1900
1901 QString expression =
1902 u"[&]() { \nauto tmpMax = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == 1) ? arg2 : ((arg2 > arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1903 for (int i = 2; i < argc; i++) {
1904 expression +=
1905 "\ttmpMax = (qIsNull(%1) && qIsNull(tmpMax) && std::copysign(1.0, %1) == 1) ? arg2 : ((%1 > tmpMax || std::isnan(%1)) ? %1 : tmpMax);\n"_L1
1906 .arg(args: "arg"_L1 + QString::number(i + 1));
1907 }
1908 expression += "return tmpMax;\n}()"_L1;
1909
1910 return expression;
1911}
1912
1913static QString minExpression(int argc)
1914{
1915 Q_ASSERT_X(argc >= 2, Q_FUNC_INFO, "min() expects at least two arguments.");
1916
1917 QString expression =
1918 u"[&]() { \nauto tmpMin = (qIsNull(arg2) && qIsNull(arg1) && std::copysign(1.0, arg2) == -1) ? arg2 : ((arg2 < arg1 || std::isnan(arg2)) ? arg2 : arg1);\n"_s;
1919 for (int i = 2; i < argc; i++) {
1920 expression +=
1921 "tmpMin = (qIsNull(%1) && qIsNull(tmpMin) && std::copysign(1.0, %1) == -1) ? arg2 : ((%1 < tmpMin || std::isnan(%1)) ? %1 : tmpMin);\n"_L1
1922 .arg(args: "arg"_L1 + QString::number(i + 1));
1923 }
1924 expression += "return tmpMin;\n}()"_L1;
1925
1926 return expression;
1927}
1928
1929bool QQmlJSCodeGenerator::inlineMathMethod(const QString &name, int argc, int argv)
1930{
1931 addInclude(include: u"cmath"_s);
1932 addInclude(include: u"limits"_s);
1933 addInclude(include: u"QtCore/qalgorithms.h"_s);
1934 addInclude(include: u"QtCore/qrandom.h"_s);
1935 addInclude(include: u"QtQml/qjsprimitivevalue.h"_s);
1936
1937 // If the result is not stored, we don't need to generate any code. All the math methods are
1938 // conceptually pure functions.
1939 if (m_state.changedRegisterIndex() != Accumulator)
1940 return true;
1941
1942 m_body += u"{\n"_s;
1943 for (int i = 0; i < argc; ++i) {
1944 m_body += u"const double arg%1 = "_s.arg(a: i + 1) + convertStored(
1945 from: registerType(index: argv + i).storedType(),
1946 to: m_typeResolver->realType(), variable: consumedRegisterVariable(index: argv + i))
1947 + u";\n"_s;
1948 }
1949
1950 const QString qNaN = u"std::numeric_limits<double>::quiet_NaN()"_s;
1951 const QString inf = u"std::numeric_limits<double>::infinity()"_s;
1952 m_body += m_state.accumulatorVariableOut + u" = "_s;
1953
1954 QString expression;
1955
1956 if (name == u"abs" && argc == 1) {
1957 expression = u"(qIsNull(arg1) ? 0 : (arg1 < 0.0 ? -arg1 : arg1))"_s;
1958 } else if (name == u"acos"_s && argc == 1) {
1959 expression = u"arg1 > 1.0 ? %1 : std::acos(arg1)"_s.arg(a: qNaN);
1960 } else if (name == u"acosh"_s && argc == 1) {
1961 expression = u"arg1 < 1.0 ? %1 : std::acosh(arg1)"_s.arg(a: qNaN);
1962 } else if (name == u"asin"_s && argc == 1) {
1963 expression = u"arg1 > 1.0 ? %1 : std::asin(arg1)"_s.arg(a: qNaN);
1964 } else if (name == u"asinh"_s && argc == 1) {
1965 expression = u"qIsNull(arg1) ? arg1 : std::asinh(arg1)"_s;
1966 } else if (name == u"atan"_s && argc == 1) {
1967 expression = u"qIsNull(arg1) ? arg1 : std::atan(arg1)"_s;
1968 } else if (name == u"atanh"_s && argc == 1) {
1969 expression = u"qIsNull(arg1) ? arg1 : std::atanh(arg1)"_s;
1970 } else if (name == u"atan2"_s) {
1971 // TODO: complicated
1972 return false;
1973 } else if (name == u"cbrt"_s && argc == 1) {
1974 expression = u"std::cbrt(arg1)"_s;
1975 } else if (name == u"ceil"_s && argc == 1) {
1976 expression = u"(arg1 < 0.0 && arg1 > -1.0) ? std::copysign(0.0, -1.0) : std::ceil(arg1)"_s;
1977 } else if (name == u"clz32"_s && argc == 1) {
1978 expression = u"qint32(qCountLeadingZeroBits(quint32(QJSNumberCoercion::toInteger(arg1))))"_s;
1979 } else if (name == u"cos"_s && argc == 1) {
1980 expression = u"std::cos(arg1)"_s;
1981 } else if (name == u"cosh"_s && argc == 1) {
1982 expression = u"std::cosh(arg1)"_s;
1983 } else if (name == u"exp"_s && argc == 1) {
1984 expression = u"std::isinf(arg1) "
1985 "? (std::copysign(1.0, arg1) == -1 ? 0.0 : %1) "
1986 ": std::exp(arg1)"_s.arg(a: inf);
1987 } else if (name == u"expm1"_s) {
1988 // TODO: complicated
1989 return false;
1990 } else if (name == u"floor"_s && argc == 1) {
1991 expression = u"std::floor(arg1)"_s;
1992 } else if (name == u"fround"_s && argc == 1) {
1993 expression = u"(std::isnan(arg1) || std::isinf(arg1) || qIsNull(arg1)) "
1994 "? arg1 "
1995 ": double(float(arg1))"_s;
1996 } else if (name == u"hypot"_s) {
1997 // TODO: complicated
1998 return false;
1999 } else if (name == u"imul"_s && argc == 2) {
2000 expression = u"qint32(quint32(QJSNumberCoercion::toInteger(arg1)) "
2001 "* quint32(QJSNumberCoercion::toInteger(arg2)))"_s;
2002 } else if (name == u"log"_s && argc == 1) {
2003 expression = u"arg1 < 0.0 ? %1 : std::log(arg1)"_s.arg(a: qNaN);
2004 } else if (name == u"log10"_s && argc == 1) {
2005 expression = u"arg1 < 0.0 ? %1 : std::log10(arg1)"_s.arg(a: qNaN);
2006 } else if (name == u"log1p"_s && argc == 1) {
2007 expression = u"arg1 < -1.0 ? %1 : std::log1p(arg1)"_s.arg(a: qNaN);
2008 } else if (name == u"log2"_s && argc == 1) {
2009 expression = u"arg1 < -0.0 ? %1 : std::log2(arg1)"_s.arg(a: qNaN);
2010 } else if (name == u"max"_s && argc >= 2) {
2011 expression = maxExpression(argc);
2012 } else if (name == u"min"_s && argc >= 2) {
2013 expression = minExpression(argc);
2014 } else if (name == u"pow"_s) {
2015 expression = u"QQmlPrivate::jsExponentiate(arg1, arg2)"_s;
2016 } else if (name == u"random"_s && argc == 0) {
2017 expression = u"QRandomGenerator::global()->generateDouble()"_s;
2018 } else if (name == u"round"_s && argc == 1) {
2019 expression = u"std::isfinite(arg1) "
2020 "? ((arg1 < 0.5 && arg1 >= -0.5) "
2021 "? std::copysign(0.0, arg1) "
2022 ": std::floor(arg1 + 0.5)) "
2023 ": arg1"_s;
2024 } else if (name == u"sign"_s && argc == 1) {
2025 expression = u"std::isnan(arg1) "
2026 "? %1 "
2027 ": (qIsNull(arg1) "
2028 "? arg1 "
2029 ": (std::signbit(arg1) ? -1.0 : 1.0))"_s.arg(a: qNaN);
2030 } else if (name == u"sin"_s && argc == 1) {
2031 expression = u"qIsNull(arg1) ? arg1 : std::sin(arg1)"_s;
2032 } else if (name == u"sinh"_s && argc == 1) {
2033 expression = u"qIsNull(arg1) ? arg1 : std::sinh(arg1)"_s;
2034 } else if (name == u"sqrt"_s && argc == 1) {
2035 expression = u"std::sqrt(arg1)"_s;
2036 } else if (name == u"tan"_s && argc == 1) {
2037 expression = u"qIsNull(arg1) ? arg1 : std::tan(arg1)"_s;
2038 } else if (name == u"tanh"_s && argc == 1) {
2039 expression = u"qIsNull(arg1) ? arg1 : std::tanh(arg1)"_s;
2040 } else if (name == u"trunc"_s && argc == 1) {
2041 expression = u"std::trunc(arg1)"_s;
2042 } else {
2043 return false;
2044 }
2045
2046 m_body += conversion(from: m_typeResolver->realType(), to: m_state.accumulatorOut(), variable: expression);
2047
2048 m_body += u";\n"_s;
2049 m_body += u"}\n"_s;
2050 return true;
2051}
2052
2053static QString messageTypeForMethod(const QString &method)
2054{
2055 if (method == u"log" || method == u"debug")
2056 return u"QtDebugMsg"_s;
2057 if (method == u"info")
2058 return u"QtInfoMsg"_s;
2059 if (method == u"warn")
2060 return u"QtWarningMsg"_s;
2061 if (method == u"error")
2062 return u"QtCriticalMsg"_s;
2063 return QString();
2064}
2065
2066bool QQmlJSCodeGenerator::inlineConsoleMethod(const QString &name, int argc, int argv)
2067{
2068 const QString type = messageTypeForMethod(method: name);
2069 if (type.isEmpty())
2070 return false;
2071
2072 addInclude(include: u"QtCore/qloggingcategory.h"_s);
2073
2074 m_body += u"{\n";
2075 m_body += u" bool firstArgIsCategory = false;\n";
2076 const QQmlJSRegisterContent firstArg = argc > 0 ? registerType(index: argv) : QQmlJSRegisterContent();
2077
2078 // We could check whether the first argument is a QQmlLoggingCategoryBase here, and we should
2079 // because QQmlLoggingCategoryBase is now a builtin.
2080 // TODO: The run time check for firstArg is obsolete.
2081 const bool firstArgIsReference = argc > 0
2082 && m_typeResolver->containedType(container: firstArg)->isReferenceType();
2083
2084 if (firstArgIsReference) {
2085 m_body += u" QObject *firstArg = ";
2086 m_body += convertStored(
2087 from: firstArg.storedType(),
2088 to: m_typeResolver->genericType(type: firstArg.storedType()),
2089 variable: registerVariable(index: argv));
2090 m_body += u";\n";
2091 }
2092
2093 m_body += u" const QLoggingCategory *category = aotContext->resolveLoggingCategory(";
2094 m_body += firstArgIsReference ? u"firstArg" : u"nullptr";
2095 m_body += u", &firstArgIsCategory);\n";
2096 m_body += u" if (category && category->isEnabled(" + type + u")) {\n";
2097
2098 m_body += u" const QString message = ";
2099
2100 const auto stringConversion = [&](int i) -> QString {
2101 const QQmlJSScope::ConstPtr read = m_state.readRegister(registerIndex: argv + i).storedType();
2102 const QQmlJSScope::ConstPtr actual = registerType(index: argv + i).storedType();
2103 if (m_typeResolver->equals(a: read, b: m_typeResolver->stringType())) {
2104 return convertStored(from: actual, to: read, variable: consumedRegisterVariable(index: argv + i));
2105 } else if (actual->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2106 addInclude(include: u"QtQml/qjslist.h"_s);
2107 return u"u'[' + QJSList(&"_s + registerVariable(index: argv + i)
2108 + u", aotContext->engine).toString() + u']'"_s;
2109 } else {
2110 reject(thing: u"converting arguments for console method to string"_s);
2111 return QString();
2112 }
2113 };
2114
2115 if (argc > 0) {
2116 if (firstArgIsReference) {
2117 const QString firstArgStringConversion = convertStored(
2118 from: registerType(index: argv).storedType(),
2119 to: m_typeResolver->stringType(), variable: registerVariable(index: argv));
2120 m_body += u"(firstArgIsCategory ? QString() : (" + firstArgStringConversion;
2121 if (argc > 1)
2122 m_body += u".append(QLatin1Char(' ')))).append(";
2123 else
2124 m_body += u"))";
2125 } else {
2126 m_body += stringConversion(0);
2127 if (argc > 1)
2128 m_body += u".append(QLatin1Char(' ')).append(";
2129 }
2130
2131 for (int i = 1; i < argc; ++i) {
2132 if (i > 1)
2133 m_body += u".append(QLatin1Char(' ')).append("_s;
2134 m_body += stringConversion(i) + u')';
2135 }
2136 } else {
2137 m_body += u"QString()";
2138 }
2139 m_body += u";\n ";
2140 generateSetInstructionPointer();
2141 m_body += u" aotContext->writeToConsole(" + type + u", message, category);\n";
2142 m_body += u" }\n";
2143 m_body += u"}\n";
2144 return true;
2145}
2146
2147bool QQmlJSCodeGenerator::inlineArrayMethod(const QString &name, int base, int argc, int argv)
2148{
2149 const auto intType = m_typeResolver->int32Type();
2150 const auto valueType = registerType(index: base).storedType()->valueType();
2151 const auto boolType = m_typeResolver->boolType();
2152 const auto stringType = m_typeResolver->stringType();
2153 const auto baseType = registerType(index: base);
2154
2155 const QString baseVar = registerVariable(index: base);
2156 const QString qjsListMethod = u"QJSList(&"_s + baseVar + u", aotContext->engine)."
2157 + name + u"(";
2158
2159 addInclude(include: u"QtQml/qjslist.h"_s);
2160
2161 if (name == u"includes" && argc > 0 && argc < 3) {
2162 QString call = qjsListMethod
2163 + convertStored(from: registerType(index: argv).storedType(), to: valueType,
2164 variable: consumedRegisterVariable(index: argv));
2165 if (argc == 2) {
2166 call += u", " + convertStored(from: registerType(index: argv + 1).storedType(), to: intType,
2167 variable: consumedRegisterVariable(index: argv + 1));
2168 }
2169 call += u")";
2170
2171 m_body += m_state.accumulatorVariableOut + u" = "_s
2172 + conversion(from: boolType, to: m_state.accumulatorOut(), variable: call) + u";\n"_s;
2173 return true;
2174 }
2175
2176 if (name == u"toString" || (name == u"join" && argc < 2)) {
2177 QString call = qjsListMethod;
2178 if (argc == 1) {
2179 call += convertStored(from: registerType(index: argv).storedType(), to: stringType,
2180 variable: consumedRegisterVariable(index: argv));
2181 }
2182 call += u")";
2183
2184 m_body += m_state.accumulatorVariableOut + u" = "_s
2185 + conversion(from: stringType, to: m_state.accumulatorOut(), variable: call) + u";\n"_s;
2186 return true;
2187 }
2188
2189 if (name == u"slice" && argc < 3) {
2190 QString call = qjsListMethod;
2191 for (int i = 0; i < argc; ++i) {
2192 if (i > 0)
2193 call += u", ";
2194 call += convertStored(from: registerType(index: argv + i).storedType(), to: intType,
2195 variable: consumedRegisterVariable(index: argv + i));
2196 }
2197 call += u")";
2198
2199 const auto outType = baseType.storedType()->isListProperty()
2200 ? m_typeResolver->globalType(type: m_typeResolver->qObjectListType())
2201 : baseType;
2202
2203 m_body += m_state.accumulatorVariableOut + u" = "_s
2204 + conversion(from: outType, to: m_state.accumulatorOut(), variable: call) + u";\n"_s;
2205 return true;
2206 }
2207
2208 if ((name == u"indexOf" || name == u"lastIndexOf") && argc > 0 && argc < 3) {
2209 QString call = qjsListMethod
2210 + convertStored(from: registerType(index: argv).storedType(), to: valueType,
2211 variable: consumedRegisterVariable(index: argv));
2212 if (argc == 2) {
2213 call += u", " + convertStored(from: registerType(index: argv + 1).storedType(), to: intType,
2214 variable: consumedRegisterVariable(index: argv + 1));
2215 }
2216 call += u")";
2217
2218 m_body += m_state.accumulatorVariableOut + u" = "_s
2219 + conversion(from: intType, to: m_state.accumulatorOut(), variable: call) + u";\n"_s;
2220 return true;
2221 }
2222
2223 return false;
2224}
2225
2226void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int argc, int argv)
2227{
2228 INJECT_TRACE_INFO(generate_CallPropertyLookup);
2229
2230 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
2231 reject(thing: u"call to untyped JavaScript function"_s);
2232
2233 const QQmlJSScope::ConstPtr scope = m_state.accumulatorOut().scopeType();
2234
2235 AccumulatorConverter registers(this);
2236
2237 const QQmlJSRegisterContent baseType = registerType(index: base);
2238 const QString name = m_jsUnitGenerator->lookupName(index);
2239
2240 if (m_typeResolver->equals(a: scope, b: m_typeResolver->mathObject())) {
2241 if (inlineMathMethod(name, argc, argv))
2242 return;
2243 } else if (m_typeResolver->equals(a: scope, b: m_typeResolver->consoleObject())) {
2244 if (inlineConsoleMethod(name, argc, argv))
2245 return;
2246 } else if (m_typeResolver->equals(a: scope, b: m_typeResolver->stringType())) {
2247 if (inlineStringMethod(name, base, argc, argv))
2248 return;
2249 } else if (baseType.storedType()->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
2250 if (inlineArrayMethod(name, base, argc, argv))
2251 return;
2252 }
2253
2254 if (!scope->isReferenceType()) {
2255 // This is possible, once we establish the right kind of lookup for it
2256 reject(thing: u"call to property '%1' of %2"_s.arg(args: name, args: baseType.descriptiveName()));
2257 }
2258
2259 const QString inputPointer = resolveQObjectPointer(
2260 required: scope, actual: baseType, variable: registerVariable(index: base),
2261 errorMessage: u"Cannot call method '%1' of %2"_s.arg(a: name));
2262
2263 const QString indexString = QString::number(index);
2264
2265 m_body += u"{\n"_s;
2266
2267 QString outVar;
2268 m_body += argumentsList(argc, argv, outVar: &outVar);
2269 const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
2270 + u", "_s + inputPointer
2271 + u", args, types, "_s + QString::number(argc) + u')';
2272 const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
2273 + indexString + u')';
2274 generateLookup(lookup, initialization);
2275 generateMoveOutVar(outVar);
2276
2277 m_body += u"}\n"_s;
2278}
2279
2280void QQmlJSCodeGenerator::generate_CallName(int name, int argc, int argv)
2281{
2282 Q_UNUSED(name);
2283 Q_UNUSED(argc);
2284 Q_UNUSED(argv);
2285 reject(thing: u"CallName"_s);
2286}
2287
2288void QQmlJSCodeGenerator::generate_CallPossiblyDirectEval(int argc, int argv)
2289{
2290 Q_UNUSED(argc)
2291 Q_UNUSED(argv)
2292 BYTECODE_UNIMPLEMENTED();
2293}
2294
2295void QQmlJSCodeGenerator::generate_CallGlobalLookup(int index, int argc, int argv)
2296{
2297 Q_UNUSED(index);
2298 Q_UNUSED(argc);
2299 Q_UNUSED(argv);
2300 reject(thing: u"CallGlobalLookup"_s);
2301}
2302
2303void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int argc, int argv)
2304{
2305 INJECT_TRACE_INFO(generate_CallQmlContextPropertyLookup);
2306
2307 if (m_state.accumulatorOut().variant() == QQmlJSRegisterContent::JavaScriptReturnValue)
2308 reject(thing: u"call to untyped JavaScript function"_s);
2309
2310 if (m_typeResolver->equals(a: m_state.accumulatorOut().scopeType(),
2311 b: m_typeResolver->jsGlobalObject())) {
2312 const QString name = m_jsUnitGenerator->stringForIndex(
2313 index: m_jsUnitGenerator->lookupNameIndex(index));
2314 if (inlineTranslateMethod(name, argc, argv))
2315 return;
2316 }
2317
2318 AccumulatorConverter registers(this);
2319
2320 const QString indexString = QString::number(index);
2321
2322 m_body += u"{\n"_s;
2323 QString outVar;
2324 m_body += argumentsList(argc, argv, outVar: &outVar);
2325 const QString lookup = u"aotContext->callQmlContextPropertyLookup("_s + indexString
2326 + u", args, types, "_s + QString::number(argc) + u')';
2327 const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_s
2328 + indexString + u')';
2329 generateLookup(lookup, initialization);
2330 generateMoveOutVar(outVar);
2331
2332 m_body += u"}\n"_s;
2333}
2334
2335void QQmlJSCodeGenerator::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
2336{
2337 Q_UNUSED(func)
2338 Q_UNUSED(thisObject)
2339 Q_UNUSED(argc)
2340 Q_UNUSED(argv)
2341 BYTECODE_UNIMPLEMENTED();
2342}
2343
2344void QQmlJSCodeGenerator::generate_TailCall(int func, int thisObject, int argc, int argv)
2345{
2346 Q_UNUSED(func)
2347 Q_UNUSED(thisObject)
2348 Q_UNUSED(argc)
2349 Q_UNUSED(argv)
2350 BYTECODE_UNIMPLEMENTED();
2351}
2352
2353void QQmlJSCodeGenerator::generate_Construct(int func, int argc, int argv)
2354{
2355 INJECT_TRACE_INFO(generate_Construct);
2356 Q_UNUSED(func);
2357
2358 const auto original = m_typeResolver->original(type: m_state.accumulatorOut());
2359
2360 if (m_typeResolver->registerContains(reg: original, type: m_typeResolver->dateTimeType())) {
2361 m_body += m_state.accumulatorVariableOut + u" = ";
2362 if (argc == 0) {
2363 m_body += conversion(
2364 from: m_typeResolver->dateTimeType(), to: m_state.accumulatorOut(),
2365 variable: u"QDateTime::currentDateTime()"_s) + u";\n";
2366 return;
2367 }
2368
2369 if (argc == 1
2370 && m_typeResolver->registerContains(
2371 reg: m_state.readRegister(registerIndex: argv), type: m_typeResolver->dateTimeType())) {
2372 m_body += conversion(
2373 from: registerType(index: argv), to: m_state.readRegister(registerIndex: argv), variable: registerVariable(index: argv))
2374 + u";\n";
2375 return;
2376 }
2377
2378 QString ctorArgs;
2379 constexpr int maxArgc = 7; // year, month, day, hours, minutes, seconds, milliseconds
2380 for (int i = 0; i < std::min(a: argc, b: maxArgc); ++i) {
2381 if (i > 0)
2382 ctorArgs += u", ";
2383 ctorArgs += conversion(
2384 from: registerType(index: argv + i), to: m_state.readRegister(registerIndex: argv + i),
2385 variable: registerVariable(index: argv + i));
2386 }
2387 m_body += conversion(
2388 from: m_typeResolver->dateTimeType(), to: m_state.accumulatorOut(),
2389 variable: u"aotContext->constructDateTime("_s + ctorArgs + u')') + u";\n";
2390 return;
2391 }
2392
2393 if (m_typeResolver->registerContains(reg: original, type: m_typeResolver->variantListType())) {
2394 rejectIfBadArray();
2395
2396 if (argc == 1
2397 && m_typeResolver->registerContains(
2398 reg: m_state.readRegister(registerIndex: argv), type: m_typeResolver->realType())) {
2399 addInclude(include: u"QtQml/qjslist.h"_s);
2400
2401 const QString error = u" aotContext->engine->throwError(QJSValue::RangeError, "_s
2402 + u"QLatin1String(\"Invalid array length\"));\n"_s;
2403
2404 const QString indexName = registerVariable(index: argv);
2405 const auto indexType = m_typeResolver->containedType(container: registerType(index: argv));
2406 if (!m_typeResolver->isNativeArrayIndex(type: indexType)) {
2407 m_body += u"if (!QJSNumberCoercion::isArrayIndex("_s + indexName + u")) {\n"_s
2408 + error;
2409 generateReturnError();
2410 m_body += u"}\n"_s;
2411 } else if (!m_typeResolver->isUnsignedInteger(type: indexType)) {
2412 m_body += u"if ("_s + indexName + u" < 0) {\n"_s
2413 + error;
2414 generateReturnError();
2415 m_body += u"}\n"_s;
2416 }
2417
2418 m_body += m_state.accumulatorVariableOut + u" = "_s
2419 + m_state.accumulatorOut().storedType()->internalName() + u"();\n"_s;
2420 m_body += u"QJSList(&"_s + m_state.accumulatorVariableOut
2421 + u", aotContext->engine).resize("_s
2422 + convertStored(
2423 from: registerType(index: argv).storedType(), to: m_typeResolver->sizeType(),
2424 variable: consumedRegisterVariable(index: argv))
2425 + u");\n"_s;
2426 } else if (!m_error->isValid()) {
2427 generateArrayInitializer(argc, argv);
2428 }
2429 return;
2430 }
2431
2432 reject(thing: u"Construct"_s);
2433}
2434
2435void QQmlJSCodeGenerator::generate_ConstructWithSpread(int func, int argc, int argv)
2436{
2437 Q_UNUSED(func)
2438 Q_UNUSED(argc)
2439 Q_UNUSED(argv)
2440 BYTECODE_UNIMPLEMENTED();
2441}
2442
2443void QQmlJSCodeGenerator::generate_SetUnwindHandler(int offset)
2444{
2445 Q_UNUSED(offset)
2446 reject(thing: u"SetUnwindHandlerh"_s);
2447}
2448
2449void QQmlJSCodeGenerator::generate_UnwindDispatch()
2450{
2451 reject(thing: u"UnwindDispatch"_s);
2452}
2453
2454void QQmlJSCodeGenerator::generate_UnwindToLabel(int level, int offset)
2455{
2456 Q_UNUSED(level)
2457 Q_UNUSED(offset)
2458 BYTECODE_UNIMPLEMENTED();
2459}
2460
2461void QQmlJSCodeGenerator::generate_DeadTemporalZoneCheck(int name)
2462{
2463 Q_UNUSED(name)
2464 INJECT_TRACE_INFO(generate_DeadTemporalZoneCheck);
2465 // Nothing to do here. If we have statically asserted the dtz check in the type propagator
2466 // the value cannot be empty. Otherwise we can't get here.
2467}
2468
2469void QQmlJSCodeGenerator::generate_ThrowException()
2470{
2471 INJECT_TRACE_INFO(generate_ThrowException);
2472
2473 generateSetInstructionPointer();
2474 m_body += u"aotContext->engine->throwError("_s
2475 + conversion(from: m_state.accumulatorIn(), to: m_typeResolver->globalType(
2476 type: m_typeResolver->jsValueType()),
2477 variable: m_state.accumulatorVariableIn) + u");\n"_s;
2478 generateReturnError();
2479 m_skipUntilNextLabel = true;
2480 resetState();
2481}
2482
2483void QQmlJSCodeGenerator::generate_GetException()
2484{
2485 BYTECODE_UNIMPLEMENTED();
2486}
2487
2488void QQmlJSCodeGenerator::generate_SetException()
2489{
2490 BYTECODE_UNIMPLEMENTED();
2491}
2492
2493void QQmlJSCodeGenerator::generate_CreateCallContext()
2494{
2495 INJECT_TRACE_INFO(generate_CreateCallContext);
2496
2497 m_body += u"{\n"_s;
2498}
2499
2500void QQmlJSCodeGenerator::generate_PushCatchContext(int index, int nameIndex)
2501{
2502 Q_UNUSED(index)
2503 Q_UNUSED(nameIndex)
2504 reject(thing: u"PushCatchContext"_s);
2505}
2506
2507void QQmlJSCodeGenerator::generate_PushWithContext()
2508{
2509 BYTECODE_UNIMPLEMENTED();
2510}
2511
2512void QQmlJSCodeGenerator::generate_PushBlockContext(int index)
2513{
2514 Q_UNUSED(index)
2515 BYTECODE_UNIMPLEMENTED();
2516}
2517
2518void QQmlJSCodeGenerator::generate_CloneBlockContext()
2519{
2520 BYTECODE_UNIMPLEMENTED();
2521}
2522
2523void QQmlJSCodeGenerator::generate_PushScriptContext(int index)
2524{
2525 Q_UNUSED(index)
2526 BYTECODE_UNIMPLEMENTED();
2527}
2528
2529void QQmlJSCodeGenerator::generate_PopScriptContext()
2530{
2531 BYTECODE_UNIMPLEMENTED();
2532}
2533
2534void QQmlJSCodeGenerator::generate_PopContext()
2535{
2536 INJECT_TRACE_INFO(generate_PopContext);
2537
2538 // Add an empty block before the closing brace, in case there was a bare label before it.
2539 m_body += u"{}\n}\n"_s;
2540}
2541
2542void QQmlJSCodeGenerator::generate_GetIterator(int iterator)
2543{
2544 INJECT_TRACE_INFO(generate_GetIterator);
2545
2546 addInclude(include: u"QtQml/qjslist.h"_s);
2547 const QQmlJSRegisterContent listType = m_state.accumulatorIn();
2548 if (!listType.isList())
2549 reject(thing: u"iterator on non-list type"_s);
2550
2551 const QQmlJSRegisterContent iteratorType = m_state.accumulatorOut();
2552 if (!iteratorType.isProperty()) {
2553 reject(thing: u"using non-iterator as iterator"_s);
2554 return;
2555 }
2556
2557 const QString identifier = QString::number(iteratorType.baseLookupIndex());
2558 const QString iteratorName = m_state.accumulatorVariableOut + u"Iterator" + identifier;
2559 const QString listName = m_state.accumulatorVariableOut + u"List" + identifier;
2560
2561 m_body += u"QJSListFor"_s
2562 + (iterator == int(QQmlJS::AST::ForEachType::In) ? u"In"_s : u"Of"_s)
2563 + u"Iterator "_s + iteratorName + u";\n";
2564 m_body += m_state.accumulatorVariableOut + u" = &" + iteratorName + u";\n";
2565
2566 m_body += m_state.accumulatorVariableOut + u"->init(";
2567 if (iterator == int(QQmlJS::AST::ForEachType::In)) {
2568 if (!m_typeResolver->equals(a: iteratorType.storedType(), b: m_typeResolver->forInIteratorPtr()))
2569 reject(thing: u"using non-iterator as iterator"_s);
2570 m_body += u"QJSList(&" + m_state.accumulatorVariableIn + u", aotContext->engine)";
2571 }
2572 m_body += u");\n";
2573
2574 if (iterator == int(QQmlJS::AST::ForEachType::Of)) {
2575 if (!m_typeResolver->equals(a: iteratorType.storedType(), b: m_typeResolver->forOfIteratorPtr()))
2576 reject(thing: u"using non-iterator as iterator"_s);
2577 m_body += u"const auto &" // Rely on life time extension for const refs
2578 + listName + u" = " + consumedAccumulatorVariableIn();
2579 }
2580}
2581
2582void QQmlJSCodeGenerator::generate_IteratorNext(int value, int offset)
2583{
2584 INJECT_TRACE_INFO(generate_IteratorNext);
2585
2586 Q_ASSERT(value == m_state.changedRegisterIndex());
2587 const QQmlJSRegisterContent iteratorContent = m_state.accumulatorIn();
2588 if (!iteratorContent.isProperty()) {
2589 reject(thing: u"using non-iterator as iterator"_s);
2590 return;
2591 }
2592
2593 const QQmlJSScope::ConstPtr iteratorType = iteratorContent.storedType();
2594 const QString iteratorTypeName = iteratorType->internalName();
2595 const QString listName = m_state.accumulatorVariableIn
2596 + u"List" + QString::number(iteratorContent.baseLookupIndex());
2597 QString qjsList;
2598 if (m_typeResolver->equals(a: iteratorType, b: m_typeResolver->forOfIteratorPtr()))
2599 qjsList = u"QJSList(&" + listName + u", aotContext->engine)";
2600 else if (!m_typeResolver->equals(a: iteratorType, b: m_typeResolver->forInIteratorPtr()))
2601 reject(thing: u"using non-iterator as iterator"_s);
2602
2603 m_body += u"if (" + m_state.accumulatorVariableIn + u"->hasNext(" + qjsList + u")) {\n ";
2604 m_body += changedRegisterVariable() + u" = "
2605 + conversion(
2606 from: m_typeResolver->valueType(list: iteratorContent),
2607 to: m_state.changedRegister(),
2608 variable: m_state.accumulatorVariableIn + u"->next(" + qjsList + u')')
2609 + u";\n";
2610 m_body += u"} else {\n ";
2611 m_body += changedRegisterVariable() + u" = "
2612 + conversion(from: m_typeResolver->voidType(), to: m_state.changedRegister(), variable: QString());
2613 m_body += u";\n ";
2614 generateJumpCodeWithTypeConversions(relativeOffset: offset);
2615 m_body += u"\n}"_s;
2616}
2617
2618void QQmlJSCodeGenerator::generate_IteratorNextForYieldStar(int iterator, int object, int offset)
2619{
2620 Q_UNUSED(iterator)
2621 Q_UNUSED(object)
2622 Q_UNUSED(offset)
2623 BYTECODE_UNIMPLEMENTED();
2624}
2625
2626void QQmlJSCodeGenerator::generate_IteratorClose()
2627{
2628 BYTECODE_UNIMPLEMENTED();
2629}
2630
2631void QQmlJSCodeGenerator::generate_DestructureRestElement()
2632{
2633 BYTECODE_UNIMPLEMENTED();
2634}
2635
2636void QQmlJSCodeGenerator::generate_DeleteProperty(int base, int index)
2637{
2638 Q_UNUSED(base)
2639 Q_UNUSED(index)
2640 BYTECODE_UNIMPLEMENTED();
2641}
2642
2643void QQmlJSCodeGenerator::generate_DeleteName(int name)
2644{
2645 Q_UNUSED(name)
2646 BYTECODE_UNIMPLEMENTED();
2647}
2648
2649void QQmlJSCodeGenerator::generate_TypeofName(int name)
2650{
2651 Q_UNUSED(name);
2652 reject(thing: u"TypeofName"_s);
2653}
2654
2655void QQmlJSCodeGenerator::generate_TypeofValue()
2656{
2657 reject(thing: u"TypeofValue"_s);
2658}
2659
2660void QQmlJSCodeGenerator::generate_DeclareVar(int varName, int isDeletable)
2661{
2662 Q_UNUSED(varName)
2663 Q_UNUSED(isDeletable)
2664 BYTECODE_UNIMPLEMENTED();
2665}
2666
2667void QQmlJSCodeGenerator::generate_DefineArray(int argc, int args)
2668{
2669 INJECT_TRACE_INFO(generate_DefineArray);
2670
2671 rejectIfBadArray();
2672 if (!m_error->isValid())
2673 generateArrayInitializer(argc, argv: args);
2674}
2675
2676void QQmlJSCodeGenerator::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
2677{
2678 INJECT_TRACE_INFO(generate_DefineObjectLiteral);
2679
2680 const QQmlJSScope::ConstPtr stored = m_state.accumulatorOut().storedType();
2681 if (stored->accessSemantics() != QQmlJSScope::AccessSemantics::Value) {
2682 reject(thing: u"storing an object literal in a non-value type"_s);
2683 return;
2684 }
2685
2686 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(container: m_state.accumulatorOut());
2687
2688 const int classSize = m_jsUnitGenerator->jsClassSize(jsClassId: internalClassId);
2689 Q_ASSERT(argc >= classSize);
2690
2691 if (m_typeResolver->equals(a: contained, b: m_typeResolver->varType())
2692 || m_typeResolver->equals(a: contained, b: m_typeResolver->variantMapType())) {
2693
2694 m_body += m_state.accumulatorVariableOut + u" = QVariantMap {\n";
2695 const QQmlJSScope::ConstPtr propType = m_typeResolver->varType();
2696 for (int i = 0; i < classSize; ++i) {
2697 m_body += u"{ "_s
2698 + QQmlJSUtils::toLiteral(s: m_jsUnitGenerator->jsClassMember(jsClassId: internalClassId, member: i))
2699 + u", "_s;
2700 const int currentArg = args + i;
2701 const QQmlJSScope::ConstPtr argType = registerType(index: currentArg).storedType();
2702 const QString consumedArg = consumedRegisterVariable(index: currentArg);
2703 m_body += convertStored(from: argType, to: propType, variable: consumedArg) + u" },\n";
2704 }
2705
2706 for (int i = classSize; i < argc; i += 3) {
2707 const int nameArg = args + i + 1;
2708 m_body += u"{ "_s
2709 + conversion(
2710 from: registerType(index: nameArg),
2711 to: m_typeResolver->globalType(type: m_typeResolver->stringType()),
2712 variable: consumedRegisterVariable(index: nameArg))
2713 + u", "_s;
2714
2715 const int valueArg = args + i + 2;
2716 m_body += convertStored(
2717 from: registerType(index: valueArg).storedType(),
2718 to: propType,
2719 variable: consumedRegisterVariable(index: valueArg))
2720 + u" },\n";
2721 }
2722
2723 m_body += u"};\n";
2724 return;
2725 }
2726
2727 m_body += m_state.accumulatorVariableOut + u" = "_s + stored->augmentedInternalName();
2728 const bool isVariantOrPrimitive = m_typeResolver->equals(a: stored, b: m_typeResolver->varType())
2729 || m_typeResolver->equals(a: stored, b: m_typeResolver->jsPrimitiveType());
2730
2731 if (m_typeResolver->registerContains(reg: m_state.accumulatorOut(), type: stored)) {
2732 m_body += u"()";
2733 } else if (isVariantOrPrimitive) {
2734 m_body += u'(' + metaType(type: m_typeResolver->containedType(container: m_state.accumulatorOut())) + u')';
2735 } else {
2736 reject(thing: u"storing an object literal in an unsupported container %1"_s
2737 .arg(a: stored->internalName()));
2738 }
2739 m_body += u";\n";
2740
2741 if (argc == 0)
2742 return;
2743
2744 bool isExtension = false;
2745 if (!m_typeResolver->canPopulate(type: contained, argument: m_typeResolver->variantMapType(), isExtension: &isExtension)) {
2746 reject(thing: u"storing an object literal in a non-structured value type"_s);
2747 }
2748
2749 const QQmlJSScope::ConstPtr accessor = isExtension
2750 ? contained->extensionType().scope
2751 : contained;
2752
2753 m_body += u"{\n";
2754 m_body += u" const QMetaObject *meta = ";
2755 if (!isExtension && isVariantOrPrimitive)
2756 m_body += m_state.accumulatorVariableOut + u".metaType().metaObject()";
2757 else
2758 m_body += metaObject(objectType: accessor);
2759 m_body += u";\n";
2760
2761 for (int i = 0; i < classSize; ++i) {
2762 m_body += u" {\n";
2763 const QString propName = m_jsUnitGenerator->jsClassMember(jsClassId: internalClassId, member: i);
2764 const int currentArg = args + i;
2765 const QQmlJSRegisterContent propType = m_state.readRegister(registerIndex: currentArg);
2766 const QQmlJSRegisterContent argType = registerType(index: currentArg);
2767 const QQmlJSMetaProperty property = contained->property(name: propName);
2768 const QString consumedArg = consumedRegisterVariable(index: currentArg);
2769 QString argument = conversion(from: argType, to: propType, variable: consumedArg);
2770
2771 if (argument == consumedArg) {
2772 argument = registerVariable(index: currentArg);
2773 } else {
2774 m_body += u" auto arg = "_s + argument + u";\n";
2775 argument = u"arg"_s;
2776 }
2777
2778 int index = property.index();
2779 if (index == -1)
2780 continue;
2781
2782 m_body += u" void *argv[] = { %1, nullptr };\n"_s
2783 .arg(a: contentPointer(content: propType, var: argument));
2784 m_body += u" meta->d.static_metacall(reinterpret_cast<QObject *>(";
2785 m_body += contentPointer(content: m_state.accumulatorOut(), var: m_state.accumulatorVariableOut);
2786 m_body += u"), QMetaObject::WriteProperty, ";
2787 m_body += QString::number(index) + u", argv);\n";
2788 m_body += u" }\n";
2789 }
2790
2791 // This is not implemented because we cannot statically determine the type of the value and we
2792 // don't want to rely on QVariant::convert() since that may give different results than
2793 // the JavaScript coercion. We might still make it work by querying the QMetaProperty
2794 // for its type at run time and runtime coercing to that, but we don't know whether that
2795 // still pays off.
2796 if (argc > classSize)
2797 reject(thing: u"non-literal keys of object literals"_s);
2798
2799 m_body += u"}\n";
2800
2801}
2802
2803void QQmlJSCodeGenerator::generate_CreateClass(int classIndex, int heritage, int computedNames)
2804{
2805 Q_UNUSED(classIndex)
2806 Q_UNUSED(heritage)
2807 Q_UNUSED(computedNames)
2808 BYTECODE_UNIMPLEMENTED();
2809}
2810
2811void QQmlJSCodeGenerator::generate_CreateMappedArgumentsObject()
2812{
2813 BYTECODE_UNIMPLEMENTED();
2814}
2815
2816void QQmlJSCodeGenerator::generate_CreateUnmappedArgumentsObject()
2817{
2818 BYTECODE_UNIMPLEMENTED();
2819}
2820
2821void QQmlJSCodeGenerator::generate_CreateRestParameter(int argIndex)
2822{
2823 Q_UNUSED(argIndex)
2824 BYTECODE_UNIMPLEMENTED();
2825}
2826
2827void QQmlJSCodeGenerator::generate_ConvertThisToObject()
2828{
2829 INJECT_TRACE_INFO(generate_ConvertThisToObject);
2830
2831 m_body += changedRegisterVariable() + u" = "_s
2832 + conversion(from: m_typeResolver->qObjectType(), to: m_state.changedRegister(),
2833 variable: u"aotContext->thisObject()"_s)
2834 + u";\n"_s;
2835}
2836
2837void QQmlJSCodeGenerator::generate_LoadSuperConstructor()
2838{
2839 BYTECODE_UNIMPLEMENTED();
2840}
2841
2842void QQmlJSCodeGenerator::generate_ToObject()
2843{
2844 BYTECODE_UNIMPLEMENTED();
2845}
2846
2847void QQmlJSCodeGenerator::generate_Jump(int offset)
2848{
2849 INJECT_TRACE_INFO(generate_Jump);
2850
2851 generateJumpCodeWithTypeConversions(relativeOffset: offset);
2852 m_skipUntilNextLabel = true;
2853 resetState();
2854}
2855
2856void QQmlJSCodeGenerator::generate_JumpTrue(int offset)
2857{
2858 INJECT_TRACE_INFO(generate_JumpTrue);
2859
2860 m_body += u"if ("_s;
2861 m_body += convertStored(from: m_state.accumulatorIn().storedType(), to: m_typeResolver->boolType(),
2862 variable: m_state.accumulatorVariableIn);
2863 m_body += u") "_s;
2864 generateJumpCodeWithTypeConversions(relativeOffset: offset);
2865}
2866
2867void QQmlJSCodeGenerator::generate_JumpFalse(int offset)
2868{
2869 INJECT_TRACE_INFO(generate_JumpFalse);
2870
2871 m_body += u"if (!"_s;
2872 m_body += convertStored(from: m_state.accumulatorIn().storedType(), to: m_typeResolver->boolType(),
2873 variable: m_state.accumulatorVariableIn);
2874 m_body += u") "_s;
2875 generateJumpCodeWithTypeConversions(relativeOffset: offset);
2876}
2877
2878void QQmlJSCodeGenerator::generate_JumpNoException(int offset)
2879{
2880 INJECT_TRACE_INFO(generate_JumpNoException);
2881
2882 m_body += u"if (!context->engine->hasException()) "_s;
2883 generateJumpCodeWithTypeConversions(relativeOffset: offset);
2884}
2885
2886void QQmlJSCodeGenerator::generate_JumpNotUndefined(int offset)
2887{
2888 Q_UNUSED(offset)
2889 BYTECODE_UNIMPLEMENTED();
2890}
2891
2892void QQmlJSCodeGenerator::generate_CheckException()
2893{
2894 INJECT_TRACE_INFO(generate_CheckException);
2895
2896 generateExceptionCheck();
2897}
2898
2899void QQmlJSCodeGenerator::generate_CmpEqNull()
2900{
2901 INJECT_TRACE_INFO(generate_CmpEqNull);
2902 generateEqualityOperation(
2903 lhsContent: m_typeResolver->globalType(type: m_typeResolver->nullType()), lhsName: QString(), function: u"equals"_s, invert: false);
2904}
2905
2906void QQmlJSCodeGenerator::generate_CmpNeNull()
2907{
2908 INJECT_TRACE_INFO(generate_CmlNeNull);
2909 generateEqualityOperation(
2910 lhsContent: m_typeResolver->globalType(type: m_typeResolver->nullType()), lhsName: QString(), function: u"equals"_s, invert: true);
2911}
2912
2913QString QQmlJSCodeGenerator::getLookupPreparation(
2914 const QQmlJSRegisterContent &content, const QString &var, int lookup)
2915{
2916 if (m_typeResolver->registerContains(reg: content, type: content.storedType()))
2917 return QString();
2918
2919 if (m_typeResolver->registerIsStoredIn(reg: content, type: m_typeResolver->varType())) {
2920 return var + u" = QVariant(aotContext->lookupResultMetaType("_s
2921 + QString::number(lookup) + u"))"_s;
2922 }
2923 // TODO: We could make sure they're compatible, for example QObject pointers.
2924 return QString();
2925}
2926
2927QString QQmlJSCodeGenerator::contentPointer(const QQmlJSRegisterContent &content, const QString &var)
2928{
2929 const QQmlJSScope::ConstPtr stored = content.storedType();
2930 if (m_typeResolver->registerContains(reg: content, type: stored))
2931 return u'&' + var;
2932
2933 if (m_typeResolver->registerIsStoredIn(reg: content, type: m_typeResolver->varType())
2934 || m_typeResolver->registerIsStoredIn(reg: content, type: m_typeResolver->jsPrimitiveType())) {
2935 return var + u".data()"_s;
2936 }
2937
2938 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2939 return u'&' + var;
2940
2941 if (m_typeResolver->isNumeric(type: content.storedType())
2942 && m_typeResolver->containedType(container: content)->scopeType() == QQmlSA::ScopeType::EnumScope) {
2943 return u'&' + var;
2944 }
2945
2946 if (stored->isListProperty() && m_typeResolver->containedType(container: content)->isListProperty())
2947 return u'&' + var;
2948
2949 reject(thing: u"content pointer of unsupported wrapper type "_s + content.descriptiveName());
2950 return QString();
2951}
2952
2953QString QQmlJSCodeGenerator::contentType(const QQmlJSRegisterContent &content, const QString &var)
2954{
2955 const QQmlJSScope::ConstPtr stored = content.storedType();
2956 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(container: content);
2957 if (m_typeResolver->equals(a: contained, b: stored))
2958 return metaTypeFromType(type: stored);
2959
2960 if (m_typeResolver->equals(a: stored, b: m_typeResolver->varType())
2961 || m_typeResolver->registerIsStoredIn(reg: content, type: m_typeResolver->jsPrimitiveType())) {
2962 return var + u".metaType()"_s; // We expect the container to be initialized
2963 }
2964
2965 if (stored->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
2966 return metaType(type: contained);
2967
2968 const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(type: contained);
2969 if (m_typeResolver->isNumeric(type: stored) && nonComposite->scopeType() == QQmlSA::ScopeType::EnumScope)
2970 return metaTypeFromType(type: nonComposite->baseType());
2971
2972 if (stored->isListProperty() && contained->isListProperty())
2973 return metaType(type: contained);
2974
2975 reject(thing: u"content type of unsupported wrapper type "_s + content.descriptiveName());
2976 return QString();
2977}
2978
2979void QQmlJSCodeGenerator::generate_CmpEqInt(int lhsConst)
2980{
2981 INJECT_TRACE_INFO(generate_CmpEqInt);
2982
2983 generateEqualityOperation(
2984 lhsContent: m_typeResolver->globalType(type: m_typeResolver->int32Type()), lhsName: QString::number(lhsConst),
2985 function: u"equals"_s, invert: false);
2986}
2987
2988void QQmlJSCodeGenerator::generate_CmpNeInt(int lhsConst)
2989{
2990 INJECT_TRACE_INFO(generate_CmpNeInt);
2991
2992 generateEqualityOperation(
2993 lhsContent: m_typeResolver->globalType(type: m_typeResolver->int32Type()), lhsName: QString::number(lhsConst),
2994 function: u"equals"_s, invert: true);
2995}
2996
2997void QQmlJSCodeGenerator::generate_CmpEq(int lhs)
2998{
2999 INJECT_TRACE_INFO(generate_CmpEq);
3000 generateEqualityOperation(lhsContent: registerType(index: lhs), lhsName: registerVariable(index: lhs), function: u"equals"_s, invert: false);
3001}
3002
3003void QQmlJSCodeGenerator::generate_CmpNe(int lhs)
3004{
3005 INJECT_TRACE_INFO(generate_CmpNe);
3006 generateEqualityOperation(lhsContent: registerType(index: lhs), lhsName: registerVariable(index: lhs), function: u"equals"_s, invert: true);
3007}
3008
3009void QQmlJSCodeGenerator::generate_CmpGt(int lhs)
3010{
3011 INJECT_TRACE_INFO(generate_CmpGt);
3012 generateCompareOperation(lhs, cppOperator: u">"_s);
3013}
3014
3015void QQmlJSCodeGenerator::generate_CmpGe(int lhs)
3016{
3017 INJECT_TRACE_INFO(generate_CmpGe);
3018 generateCompareOperation(lhs, cppOperator: u">="_s);
3019}
3020
3021void QQmlJSCodeGenerator::generate_CmpLt(int lhs)
3022{
3023 INJECT_TRACE_INFO(generate_CmpLt);
3024 generateCompareOperation(lhs, cppOperator: u"<"_s);
3025}
3026
3027void QQmlJSCodeGenerator::generate_CmpLe(int lhs)
3028{
3029 INJECT_TRACE_INFO(generate_CmpLe);
3030 generateCompareOperation(lhs, cppOperator: u"<="_s);
3031}
3032
3033void QQmlJSCodeGenerator::generate_CmpStrictEqual(int lhs)
3034{
3035 INJECT_TRACE_INFO(generate_CmpStrictEqual);
3036 generateEqualityOperation(lhsContent: registerType(index: lhs), lhsName: registerVariable(index: lhs), function: u"strictlyEquals"_s, invert: false);
3037}
3038
3039void QQmlJSCodeGenerator::generate_CmpStrictNotEqual(int lhs)
3040{
3041 INJECT_TRACE_INFO(generate_CmpStrictNotEqual);
3042 generateEqualityOperation(lhsContent: registerType(index: lhs), lhsName: registerVariable(index: lhs), function: u"strictlyEquals"_s, invert: true);
3043}
3044
3045void QQmlJSCodeGenerator::generate_CmpIn(int lhs)
3046{
3047 Q_UNUSED(lhs)
3048 reject(thing: u"CmpIn"_s);
3049}
3050
3051void QQmlJSCodeGenerator::generate_CmpInstanceOf(int lhs)
3052{
3053 Q_UNUSED(lhs)
3054 BYTECODE_UNIMPLEMENTED();
3055}
3056
3057void QQmlJSCodeGenerator::generate_As(int lhs)
3058{
3059 INJECT_TRACE_INFO(generate_As);
3060
3061 const QString input = registerVariable(index: lhs);
3062 const QQmlJSRegisterContent inputContent = m_state.readRegister(registerIndex: lhs);
3063 const QQmlJSRegisterContent outputContent = m_state.accumulatorOut();
3064
3065 // If the original output is a conversion, we're supposed to check for the contained
3066 // type and if it doesn't match, set the result to null or undefined.
3067 const QQmlJSRegisterContent originalContent = m_typeResolver->original(type: outputContent);
3068 const QQmlJSScope::ConstPtr target = originalContent.storedType()->isReferenceType()
3069 ? m_typeResolver->containedType(container: originalContent)
3070 : m_typeResolver->extractNonVoidFromOptionalType(content: originalContent);
3071
3072 if (!target) {
3073 reject(thing: u"type assertion to unknown type"_s);
3074 return;
3075 }
3076
3077 const bool isTrivial = m_typeResolver->inherits(
3078 derived: m_typeResolver->originalContainedType(container: inputContent), base: target);
3079
3080 m_body += m_state.accumulatorVariableOut + u" = "_s;
3081
3082 if (!isTrivial && target->isReferenceType()) {
3083 const QQmlJSScope::ConstPtr genericContained = m_typeResolver->genericType(type: target);
3084 const QString inputConversion = inputContent.storedType()->isReferenceType()
3085 ? input
3086 : convertStored(from: inputContent.storedType(), to: genericContained, variable: input);
3087
3088 if (target->isComposite() && m_typeResolver->equals(
3089 a: m_state.accumulatorIn().storedType(), b: m_typeResolver->metaObjectType())) {
3090 m_body += conversion(
3091 from: genericContained, to: outputContent,
3092 variable: m_state.accumulatorVariableIn + u"->cast("_s + inputConversion + u')');
3093 } else {
3094 m_body += conversion(
3095 from: genericContained, to: outputContent,
3096 variable: u'(' + metaObject(objectType: target) + u")->cast("_s + inputConversion + u')');
3097 }
3098 m_body += u";\n"_s;
3099 return;
3100 }
3101
3102 if (m_typeResolver->registerIsStoredIn(reg: inputContent, type: m_typeResolver->varType())
3103 || m_typeResolver->registerIsStoredIn(reg: inputContent, type: m_typeResolver->jsPrimitiveType())) {
3104
3105 const auto source = m_typeResolver->extractNonVoidFromOptionalType(
3106 content: m_typeResolver->original(type: inputContent));
3107
3108 if (source && m_typeResolver->equals(a: source, b: target)) {
3109 m_body += input + u".metaType() == "_s + metaType(type: target)
3110 + u" ? " + conversion(from: inputContent, to: outputContent, variable: input)
3111 + u" : " + conversion(
3112 from: m_typeResolver->globalType(type: m_typeResolver->voidType()),
3113 to: outputContent, variable: QString());
3114 m_body += u";\n"_s;
3115 return;
3116 }
3117 }
3118
3119 if (isTrivial) {
3120 // No actual conversion necessary. The 'as' is a no-op
3121 m_body += conversion(from: inputContent, to: m_state.accumulatorOut(), variable: input) + u";\n"_s;
3122 return;
3123 }
3124
3125 reject(thing: u"non-trivial value type assertion"_s);
3126}
3127
3128void QQmlJSCodeGenerator::generate_UNot()
3129{
3130 INJECT_TRACE_INFO(generate_UNot);
3131 generateUnaryOperation(cppOperator: u"!"_s);
3132}
3133
3134void QQmlJSCodeGenerator::generate_UPlus()
3135{
3136 INJECT_TRACE_INFO(generate_UPlus);
3137 generateUnaryOperation(cppOperator: u"+"_s);
3138}
3139
3140void QQmlJSCodeGenerator::generate_UMinus()
3141{
3142 INJECT_TRACE_INFO(generate_UMinus);
3143 generateUnaryOperation(cppOperator: u"-"_s);
3144}
3145
3146void QQmlJSCodeGenerator::generate_UCompl()
3147{
3148 INJECT_TRACE_INFO(generate_UCompl);
3149 generateUnaryOperation(cppOperator: u"~"_s);
3150}
3151
3152void QQmlJSCodeGenerator::generate_Increment()
3153{
3154 INJECT_TRACE_INFO(generate_Increment);
3155 generateInPlaceOperation(cppOperator: u"++"_s);
3156}
3157
3158void QQmlJSCodeGenerator::generate_Decrement()
3159{
3160 INJECT_TRACE_INFO(generate_Decrement);
3161 generateInPlaceOperation(cppOperator: u"--"_s);
3162}
3163
3164void QQmlJSCodeGenerator::generate_Add(int lhs)
3165{
3166 INJECT_TRACE_INFO(generate_Add);
3167 generateArithmeticOperation(lhs, cppOperator: u"+"_s);
3168}
3169
3170void QQmlJSCodeGenerator::generate_BitAnd(int lhs)
3171{
3172 INJECT_TRACE_INFO(generate_BitAnd);
3173 generateArithmeticOperation(lhs, cppOperator: u"&"_s);
3174}
3175
3176void QQmlJSCodeGenerator::generate_BitOr(int lhs)
3177{
3178 INJECT_TRACE_INFO(generate_BitOr);
3179 generateArithmeticOperation(lhs, cppOperator: u"|"_s);
3180}
3181
3182void QQmlJSCodeGenerator::generate_BitXor(int lhs)
3183{
3184 INJECT_TRACE_INFO(generate_BitXor);
3185 generateArithmeticOperation(lhs, cppOperator: u"^"_s);
3186}
3187
3188void QQmlJSCodeGenerator::generate_UShr(int lhs)
3189{
3190 INJECT_TRACE_INFO(generate_BitUShr);
3191 generateShiftOperation(lhs, cppOperator: u">>"_s);
3192}
3193
3194void QQmlJSCodeGenerator::generate_Shr(int lhs)
3195{
3196 INJECT_TRACE_INFO(generate_Shr);
3197 generateShiftOperation(lhs, cppOperator: u">>"_s);
3198}
3199
3200void QQmlJSCodeGenerator::generate_Shl(int lhs)
3201{
3202 INJECT_TRACE_INFO(generate_Shl);
3203 generateShiftOperation(lhs, cppOperator: u"<<"_s);
3204}
3205
3206void QQmlJSCodeGenerator::generate_BitAndConst(int rhs)
3207{
3208 INJECT_TRACE_INFO(generate_BitAndConst);
3209 generateArithmeticConstOperation(lhsConst: rhs, cppOperator: u"&"_s);
3210}
3211
3212void QQmlJSCodeGenerator::generate_BitOrConst(int rhs)
3213{
3214 INJECT_TRACE_INFO(generate_BitOrConst);
3215 generateArithmeticConstOperation(lhsConst: rhs, cppOperator: u"|"_s);
3216}
3217
3218void QQmlJSCodeGenerator::generate_BitXorConst(int rhs)
3219{
3220 INJECT_TRACE_INFO(generate_BitXorConst);
3221 generateArithmeticConstOperation(lhsConst: rhs, cppOperator: u"^"_s);
3222}
3223
3224void QQmlJSCodeGenerator::generate_UShrConst(int rhs)
3225{
3226 INJECT_TRACE_INFO(generate_UShrConst);
3227 generateArithmeticConstOperation(lhsConst: rhs & 0x1f, cppOperator: u">>"_s);
3228}
3229
3230void QQmlJSCodeGenerator::generate_ShrConst(int rhs)
3231{
3232 INJECT_TRACE_INFO(generate_ShrConst);
3233 generateArithmeticConstOperation(lhsConst: rhs & 0x1f, cppOperator: u">>"_s);
3234}
3235
3236void QQmlJSCodeGenerator::generate_ShlConst(int rhs)
3237{
3238 INJECT_TRACE_INFO(generate_ShlConst);
3239 generateArithmeticConstOperation(lhsConst: rhs & 0x1f, cppOperator: u"<<"_s);
3240}
3241
3242void QQmlJSCodeGenerator::generate_Exp(int lhs)
3243{
3244 INJECT_TRACE_INFO(generate_Exp);
3245
3246 const QString lhsString = conversion(
3247 from: registerType(index: lhs), to: m_state.readRegister(registerIndex: lhs), variable: consumedRegisterVariable(index: lhs));
3248 const QString rhsString = conversion(
3249 from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3250 variable: consumedAccumulatorVariableIn());
3251
3252 Q_ASSERT(m_error->isValid() || !lhsString.isEmpty());
3253 Q_ASSERT(m_error->isValid() || !rhsString.isEmpty());
3254
3255 const QQmlJSRegisterContent originalOut = m_typeResolver->original(type: m_state.accumulatorOut());
3256 m_body += m_state.accumulatorVariableOut + u" = "_s;
3257 m_body += conversion(
3258 from: originalOut, to: m_state.accumulatorOut(),
3259 variable: u"QQmlPrivate::jsExponentiate("_s + lhsString + u", "_s + rhsString + u')');
3260 m_body += u";\n"_s;
3261}
3262
3263void QQmlJSCodeGenerator::generate_Mul(int lhs)
3264{
3265 INJECT_TRACE_INFO(generate_Mul);
3266 generateArithmeticOperation(lhs, cppOperator: u"*"_s);
3267}
3268
3269void QQmlJSCodeGenerator::generate_Div(int lhs)
3270{
3271 INJECT_TRACE_INFO(generate_Div);
3272 generateArithmeticOperation(lhs, cppOperator: u"/"_s);
3273}
3274
3275void QQmlJSCodeGenerator::generate_Mod(int lhs)
3276{
3277 INJECT_TRACE_INFO(generate_Mod);
3278
3279 const auto lhsVar = convertStored(
3280 from: registerType(index: lhs).storedType(), to: m_typeResolver->jsPrimitiveType(),
3281 variable: consumedRegisterVariable(index: lhs));
3282 const auto rhsVar = convertStored(
3283 from: m_state.accumulatorIn().storedType(), to: m_typeResolver->jsPrimitiveType(),
3284 variable: consumedAccumulatorVariableIn());
3285 Q_ASSERT(m_error->isValid() || !lhsVar.isEmpty());
3286 Q_ASSERT(m_error->isValid() || !rhsVar.isEmpty());
3287
3288 m_body += m_state.accumulatorVariableOut;
3289 m_body += u" = "_s;
3290 m_body += conversion(from: m_typeResolver->jsPrimitiveType(), to: m_state.accumulatorOut(),
3291 variable: u'(' + lhsVar + u" % "_s + rhsVar + u')');
3292 m_body += u";\n"_s;
3293}
3294
3295void QQmlJSCodeGenerator::generate_Sub(int lhs)
3296{
3297 INJECT_TRACE_INFO(generate_Sub);
3298 generateArithmeticOperation(lhs, cppOperator: u"-"_s);
3299}
3300
3301void QQmlJSCodeGenerator::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
3302{
3303 Q_UNUSED(firstReg)
3304 Q_UNUSED(count)
3305 // Ignore. We reject uninitialized values anyway.
3306}
3307
3308void QQmlJSCodeGenerator::generate_ThrowOnNullOrUndefined()
3309{
3310 BYTECODE_UNIMPLEMENTED();
3311}
3312
3313void QQmlJSCodeGenerator::generate_GetTemplateObject(int index)
3314{
3315 Q_UNUSED(index)
3316 BYTECODE_UNIMPLEMENTED();
3317}
3318
3319QV4::Moth::ByteCodeHandler::Verdict QQmlJSCodeGenerator::startInstruction(
3320 QV4::Moth::Instr::Type type)
3321{
3322 m_state.State::operator=(nextStateFromAnnotations(oldState: m_state, annotations: m_annotations));
3323 const auto accumulatorIn = m_state.registers.find(key: Accumulator);
3324 if (accumulatorIn != m_state.registers.end()
3325 && isTypeStorable(resolver: m_typeResolver, type: accumulatorIn.value().content.storedType())) {
3326 const QQmlJSRegisterContent &content = accumulatorIn.value().content;
3327 m_state.accumulatorVariableIn = m_registerVariables.value(key: RegisterVariablesKey {
3328 .internalName: content.storedType()->internalName(),
3329 .registerIndex: Accumulator,
3330 .lookupIndex: content.resultLookupIndex()
3331 }).variableName;
3332 Q_ASSERT(!m_state.accumulatorVariableIn.isEmpty());
3333 } else {
3334 m_state.accumulatorVariableIn.clear();
3335 }
3336
3337 auto labelIt = m_labels.constFind(key: currentInstructionOffset());
3338 if (labelIt != m_labels.constEnd()) {
3339 m_body += *labelIt + u":;\n"_s;
3340 m_skipUntilNextLabel = false;
3341 } else if (m_skipUntilNextLabel && !instructionManipulatesContext(type)) {
3342 return SkipInstruction;
3343 }
3344
3345 if (m_state.changedRegisterIndex() == Accumulator)
3346 m_state.accumulatorVariableOut = changedRegisterVariable();
3347 else
3348 m_state.accumulatorVariableOut.clear();
3349
3350 // If the accumulator type is valid, we want an accumulator variable.
3351 // If not, we don't want one.
3352 Q_ASSERT(m_state.changedRegisterIndex() == Accumulator
3353 || m_state.accumulatorVariableOut.isEmpty());
3354 Q_ASSERT(m_state.changedRegisterIndex() != Accumulator
3355 || !m_state.accumulatorVariableOut.isEmpty()
3356 || !isTypeStorable(m_typeResolver, m_state.changedRegister().storedType()));
3357
3358 // If the instruction has no side effects and doesn't write any register, it's dead.
3359 // We might still need the label, though, and the source code comment.
3360 if (!m_state.hasSideEffects() && changedRegisterVariable().isEmpty()) {
3361 generateJumpCodeWithTypeConversions(relativeOffset: 0);
3362 return SkipInstruction;
3363 }
3364
3365 return ProcessInstruction;
3366}
3367
3368void QQmlJSCodeGenerator::endInstruction(QV4::Moth::Instr::Type)
3369{
3370 if (!m_skipUntilNextLabel)
3371 generateJumpCodeWithTypeConversions(relativeOffset: 0);
3372}
3373
3374void QQmlJSCodeGenerator::generateSetInstructionPointer()
3375{
3376 m_body += u"aotContext->setInstructionPointer("_s
3377 + QString::number(nextInstructionOffset()) + u");\n"_s;
3378}
3379
3380void QQmlJSCodeGenerator::generateExceptionCheck()
3381{
3382 m_body += u"if (aotContext->engine->hasError()) {\n"_s;
3383 generateReturnError();
3384 m_body += u"}\n"_s;
3385}
3386
3387void QQmlJSCodeGenerator::generateEqualityOperation(
3388 const QQmlJSRegisterContent &lhsContent, const QQmlJSRegisterContent &rhsContent,
3389 const QString &lhsName, const QString &rhsName, const QString &function, bool invert)
3390{
3391 const bool lhsIsOptional = m_typeResolver->isOptionalType(content: lhsContent);
3392 const bool rhsIsOptional = m_typeResolver->isOptionalType(content: rhsContent);
3393
3394 const auto rhsContained = rhsIsOptional
3395 ? m_typeResolver->extractNonVoidFromOptionalType(content: rhsContent)
3396 : m_typeResolver->containedType(container: rhsContent);
3397
3398 const auto lhsContained = lhsIsOptional
3399 ? m_typeResolver->extractNonVoidFromOptionalType(content: lhsContent)
3400 : m_typeResolver->containedType(container: lhsContent);
3401
3402 const bool isStrict = function == "strictlyEquals"_L1;
3403 const bool strictlyComparableWithVar
3404 = isStrict && canStrictlyCompareWithVar(typeResolver: m_typeResolver, lhsType: lhsContained, rhsType: rhsContained);
3405 auto isComparable = [&]() {
3406 if (m_typeResolver->isPrimitive(type: lhsContent) && m_typeResolver->isPrimitive(type: rhsContent))
3407 return true;
3408 if (m_typeResolver->isNumeric(type: lhsContent) && rhsContent.isEnumeration())
3409 return true;
3410 if (m_typeResolver->isNumeric(type: rhsContent) && lhsContent.isEnumeration())
3411 return true;
3412 if (strictlyComparableWithVar)
3413 return true;
3414 if (canCompareWithQObject(typeResolver: m_typeResolver, lhsType: lhsContained, rhsType: rhsContained))
3415 return true;
3416 if (canCompareWithQUrl(typeResolver: m_typeResolver, lhsType: lhsContained, rhsType: rhsContained))
3417 return true;
3418 return false;
3419 };
3420
3421 const auto retrieveOriginal = [this](const QQmlJSRegisterContent &content) {
3422 const auto contained = m_typeResolver->containedType(container: content);
3423 const auto original = m_typeResolver->original(type: content);
3424 const auto containedOriginal = m_typeResolver->containedType(container: original);
3425
3426 if (m_typeResolver->equals(
3427 a: m_typeResolver->genericType(type: containedOriginal), b: original.storedType())) {
3428 // The original type doesn't need any wrapping.
3429 return original;
3430 } else if (m_typeResolver->equals(a: contained, b: containedOriginal)) {
3431 if (original.isConversion()) {
3432 // The original conversion origins are more accurate
3433 return original.storedIn(newStoredType: content.storedType());
3434 }
3435 } else if (m_typeResolver->canHold(container: contained, contained: containedOriginal)) {
3436 return original.storedIn(newStoredType: content.storedType());
3437 }
3438
3439 return content;
3440 };
3441
3442 if (!isComparable()) {
3443 QQmlJSRegisterContent lhsOriginal = retrieveOriginal(lhsContent);
3444 QQmlJSRegisterContent rhsOriginal = retrieveOriginal(rhsContent);
3445 if (lhsOriginal != lhsContent || rhsOriginal != rhsContent) {
3446 // If either side is simply a wrapping of a specific type into a more general one, we
3447 // can compare the original types instead. You can't nest wrappings after all.
3448 generateEqualityOperation(lhsContent: lhsOriginal, rhsContent: rhsOriginal,
3449 lhsName: conversion(from: lhsContent.storedType(), to: lhsOriginal, variable: lhsName),
3450 rhsName: conversion(from: rhsContent.storedType(), to: rhsOriginal, variable: rhsName),
3451 function, invert);
3452 return;
3453 }
3454
3455 reject(thing: u"incomparable types %1 and %2"_s.arg(
3456 args: rhsContent.descriptiveName(), args: lhsContent.descriptiveName()));
3457 }
3458
3459 const QQmlJSScope::ConstPtr lhsType = lhsContent.storedType();
3460 const QQmlJSScope::ConstPtr rhsType = rhsContent.storedType();
3461
3462 if (strictlyComparableWithVar) {
3463 // Determine which side is holding a storable type
3464 if (!lhsName.isEmpty() && rhsName.isEmpty()) {
3465 // lhs register holds var type and rhs is not storable
3466 generateVariantEqualityComparison(nonStorableContent: rhsContent, registerName: lhsName, invert);
3467 return;
3468 }
3469
3470 if (!rhsName.isEmpty() && lhsName.isEmpty()) {
3471 // lhs content is not storable and rhs is var type
3472 generateVariantEqualityComparison(nonStorableContent: lhsContent, registerName: rhsName, invert);
3473 return;
3474 }
3475
3476 if (m_typeResolver->registerContains(reg: lhsContent, type: m_typeResolver->varType())) {
3477 generateVariantEqualityComparison(storableContent: rhsContent, typedRegisterName: rhsName, varRegisterName: lhsName, invert);
3478 return;
3479 }
3480
3481 if (m_typeResolver->registerContains(reg: rhsContent, type: m_typeResolver->varType())) {
3482 generateVariantEqualityComparison(storableContent: lhsContent, typedRegisterName: lhsName, varRegisterName: rhsName, invert);
3483 return;
3484 }
3485
3486 // It shouldn't be possible to get here because optional null should be stored in
3487 // QJSPrimitiveValue, not in QVariant. But let's rather be safe than sorry.
3488 reject(thing: u"comparison of optional null"_s);
3489 }
3490
3491 const auto comparison = [&]() -> QString {
3492 const auto primitive = m_typeResolver->jsPrimitiveType();
3493 const QString sign = invert ? u" != "_s : u" == "_s;
3494
3495 if (m_typeResolver->equals(a: lhsType, b: rhsType)
3496 && !m_typeResolver->equals(a: lhsType, b: primitive)
3497 && !m_typeResolver->equals(a: lhsType, b: m_typeResolver->varType())) {
3498
3499 // Straight forward comparison of equal types,
3500 // except QJSPrimitiveValue which has two comparison functions.
3501
3502 if (isTypeStorable(resolver: m_typeResolver, type: lhsType))
3503 return lhsName + sign + rhsName;
3504
3505 // null === null and undefined === undefined
3506 return invert ? u"false"_s : u"true"_s;
3507 }
3508
3509 if (canCompareWithQObject(typeResolver: m_typeResolver, lhsType, rhsType)) {
3510 // Comparison of QObject-derived with nullptr or different QObject-derived.
3511 return (isTypeStorable(resolver: m_typeResolver, type: lhsType) ? lhsName : u"nullptr"_s)
3512 + sign
3513 + (isTypeStorable(resolver: m_typeResolver, type: rhsType) ? rhsName : u"nullptr"_s);
3514 }
3515
3516 if (canCompareWithQObject(typeResolver: m_typeResolver, lhsType: lhsContained, rhsType: rhsContained)) {
3517 // Comparison of optional QObject-derived with nullptr or different QObject-derived.
3518 // Mind that null == undefined but null !== undefined
3519 // Therefore the isStrict dance.
3520
3521 QString result;
3522 if (isStrict) {
3523 if (lhsIsOptional) {
3524 if (rhsIsOptional) {
3525 // If both are invalid we're fine
3526 result += u"(!"_s
3527 + lhsName + u".isValid() && !"_s
3528 + rhsName + u".isValid()) || "_s;
3529 }
3530
3531 result += u'(' + lhsName + u".isValid() && "_s;
3532 } else {
3533 result += u'(';
3534 }
3535
3536 if (rhsIsOptional) {
3537 result += rhsName + u".isValid() && "_s;
3538 }
3539 } else {
3540 result += u'(';
3541 }
3542
3543 // We do not implement comparison with explicit undefined, yet. Only with null.
3544 Q_ASSERT(!m_typeResolver->equals(lhsType, m_typeResolver->voidType()));
3545 Q_ASSERT(!m_typeResolver->equals(rhsType, m_typeResolver->voidType()));
3546
3547 const auto resolvedName = [&](const QString name) -> QString {
3548 // If isStrict we check validity already before.
3549 const QString content = u"*static_cast<QObject **>("_s + name + u".data())"_s;
3550 return isStrict
3551 ? content
3552 : u'(' + name + u".isValid() ? "_s + content + u" : nullptr)"_s;
3553 };
3554
3555 const QString lhsResolved = lhsIsOptional ? resolvedName(lhsName) : lhsName;
3556 const QString rhsResolved = rhsIsOptional ? resolvedName(rhsName) : rhsName;
3557
3558 return (invert ? u"!("_s : u"("_s) + result
3559 + (isTypeStorable(resolver: m_typeResolver, type: lhsType) ? lhsResolved : u"nullptr"_s)
3560 + u" == "_s
3561 + (isTypeStorable(resolver: m_typeResolver, type: rhsType) ? rhsResolved : u"nullptr"_s)
3562 + u"))"_s;
3563 }
3564
3565 if ((m_typeResolver->isUnsignedInteger(type: rhsType)
3566 && m_typeResolver->isUnsignedInteger(type: lhsType))
3567 || (m_typeResolver->isSignedInteger(type: rhsType)
3568 && m_typeResolver->isSignedInteger(type: lhsType))) {
3569 // Both integers of same signedness: Let the C++ compiler perform the type promotion
3570 return lhsName + sign + rhsName;
3571 }
3572
3573 if (m_typeResolver->equals(a: rhsType, b: m_typeResolver->boolType())
3574 && m_typeResolver->isIntegral(type: lhsType)) {
3575 // Integral and bool: We can promote the bool to the integral type
3576 return lhsName + sign + convertStored(from: rhsType, to: lhsType, variable: rhsName);
3577 }
3578
3579 if (m_typeResolver->equals(a: lhsType, b: m_typeResolver->boolType())
3580 && m_typeResolver->isIntegral(type: rhsType)) {
3581 // Integral and bool: We can promote the bool to the integral type
3582 return convertStored(from: lhsType, to: rhsType, variable: lhsName) + sign + rhsName;
3583 }
3584
3585 if (m_typeResolver->isNumeric(type: lhsType) && m_typeResolver->isNumeric(type: rhsType)) {
3586 // Both numbers: promote them to double
3587 return convertStored(from: lhsType, to: m_typeResolver->realType(), variable: lhsName)
3588 + sign
3589 + convertStored(from: rhsType, to: m_typeResolver->realType(), variable: rhsName);
3590 }
3591
3592 // If none of the above matches, we have to use QJSPrimitiveValue
3593 return (invert ? u"!"_s : QString())
3594 + convertStored(from: lhsType, to: primitive, variable: lhsName)
3595 + u'.' + function + u'(' + convertStored(from: rhsType, to: primitive, variable: rhsName) + u')';
3596 };
3597
3598 m_body += m_state.accumulatorVariableOut + u" = "_s;
3599 m_body += conversion(from: m_typeResolver->boolType(), to: m_state.accumulatorOut(), variable: comparison());
3600 m_body += u";\n"_s;
3601}
3602
3603void QQmlJSCodeGenerator::generateCompareOperation(int lhs, const QString &cppOperator)
3604{
3605 m_body += m_state.accumulatorVariableOut + u" = "_s;
3606
3607 const auto lhsType = registerType(index: lhs);
3608 const QQmlJSScope::ConstPtr compareType =
3609 m_typeResolver->isNumeric(type: lhsType) && m_typeResolver->isNumeric(type: m_state.accumulatorIn())
3610 ? m_typeResolver->merge(a: lhsType, b: m_state.accumulatorIn()).storedType()
3611 : m_typeResolver->jsPrimitiveType();
3612
3613 m_body += conversion(
3614 from: m_typeResolver->boolType(), to: m_state.accumulatorOut(),
3615 variable: convertStored(from: registerType(index: lhs).storedType(), to: compareType,
3616 variable: consumedRegisterVariable(index: lhs))
3617 + u' ' + cppOperator + u' '
3618 + convertStored(from: m_state.accumulatorIn().storedType(), to: compareType,
3619 variable: consumedAccumulatorVariableIn()));
3620 m_body += u";\n"_s;
3621}
3622
3623void QQmlJSCodeGenerator::generateArithmeticOperation(int lhs, const QString &cppOperator)
3624{
3625 generateArithmeticOperation(
3626 lhs: conversion(from: registerType(index: lhs), to: m_state.readRegister(registerIndex: lhs),
3627 variable: consumedRegisterVariable(index: lhs)),
3628 rhs: conversion(from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3629 variable: consumedAccumulatorVariableIn()),
3630 cppOperator);
3631}
3632
3633void QQmlJSCodeGenerator::generateShiftOperation(int lhs, const QString &cppOperator)
3634{
3635 generateArithmeticOperation(
3636 lhs: conversion(from: registerType(index: lhs), to: m_state.readRegister(registerIndex: lhs),
3637 variable: consumedRegisterVariable(index: lhs)),
3638 rhs: u'(' + conversion(from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3639 variable: consumedAccumulatorVariableIn()) + u" & 0x1f)"_s,
3640 cppOperator);
3641}
3642
3643void QQmlJSCodeGenerator::generateArithmeticOperation(
3644 const QString &lhs, const QString &rhs, const QString &cppOperator)
3645{
3646 Q_ASSERT(m_error->isValid() || !lhs.isEmpty());
3647 Q_ASSERT(m_error->isValid() || !rhs.isEmpty());
3648
3649 const QQmlJSRegisterContent originalOut = m_typeResolver->original(type: m_state.accumulatorOut());
3650 m_body += m_state.accumulatorVariableOut;
3651 m_body += u" = "_s;
3652 const QString explicitCast
3653 = m_typeResolver->equals(a: originalOut.storedType(), b: m_typeResolver->stringType())
3654 ? originalOut.storedType()->internalName()
3655 : QString();
3656 m_body += conversion(
3657 from: originalOut, to: m_state.accumulatorOut(),
3658 variable: explicitCast + u'(' + lhs + u' ' + cppOperator + u' ' + rhs + u')');
3659 m_body += u";\n"_s;
3660}
3661
3662void QQmlJSCodeGenerator::generateArithmeticConstOperation(int rhsConst, const QString &cppOperator)
3663{
3664 generateArithmeticOperation(
3665 lhs: conversion(from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3666 variable: consumedAccumulatorVariableIn()),
3667 rhs: conversion(from: m_typeResolver->globalType(type: m_typeResolver->int32Type()),
3668 to: m_state.readAccumulator(), variable: QString::number(rhsConst)),
3669 cppOperator);
3670}
3671
3672void QQmlJSCodeGenerator::generateUnaryOperation(const QString &cppOperator)
3673{
3674 const auto var = conversion(from: m_state.accumulatorIn(),
3675 to: m_typeResolver->original(type: m_state.readAccumulator()),
3676 variable: consumedAccumulatorVariableIn());
3677
3678 if (var == m_state.accumulatorVariableOut) {
3679 m_body += m_state.accumulatorVariableOut + u" = "_s + cppOperator + var + u";\n"_s;
3680 return;
3681 }
3682
3683 const auto original = m_typeResolver->original(type: m_state.accumulatorOut());
3684 if (m_state.accumulatorOut() == original) {
3685 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3686 m_body += m_state.accumulatorVariableOut + u" = "_s
3687 + cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3688 return;
3689 }
3690
3691 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3692 from: original, to: m_state.accumulatorOut(), variable: cppOperator + var) + u";\n"_s;
3693}
3694
3695void QQmlJSCodeGenerator::generateInPlaceOperation(const QString &cppOperator)
3696{
3697 {
3698 // If actually in place, we cannot consume the variable.
3699 const QString var = conversion(from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3700 variable: m_state.accumulatorVariableIn);
3701 if (var == m_state.accumulatorVariableOut) {
3702 m_body += cppOperator + var + u";\n"_s;
3703 return;
3704 }
3705 }
3706
3707 const QString var = conversion(from: m_state.accumulatorIn(), to: m_state.readAccumulator(),
3708 variable: consumedAccumulatorVariableIn());
3709
3710 const auto original = m_typeResolver->original(type: m_state.accumulatorOut());
3711 if (m_state.accumulatorOut() == original) {
3712 m_body += m_state.accumulatorVariableOut + u" = "_s + var + u";\n"_s;
3713 m_body += cppOperator + m_state.accumulatorVariableOut + u";\n"_s;
3714 return;
3715 }
3716
3717 m_body += u"{\n"_s;
3718 m_body += u"auto converted = "_s + var + u";\n"_s;
3719 m_body += m_state.accumulatorVariableOut + u" = "_s + conversion(
3720 from: original, to: m_state.accumulatorOut(), variable: u'('
3721 + cppOperator + u"converted)"_s) + u";\n"_s;
3722 m_body += u"}\n"_s;
3723}
3724
3725void QQmlJSCodeGenerator::generateLookup(const QString &lookup, const QString &initialization,
3726 const QString &resultPreparation)
3727{
3728 m_body += u"#ifndef QT_NO_DEBUG\n"_s;
3729 generateSetInstructionPointer();
3730 m_body += u"#endif\n"_s;
3731
3732 if (!resultPreparation.isEmpty())
3733 m_body += resultPreparation + u";\n"_s;
3734 m_body += u"while (!"_s + lookup + u") {\n"_s;
3735
3736 m_body += u"#ifdef QT_NO_DEBUG\n"_s;
3737 generateSetInstructionPointer();
3738 m_body += u"#endif\n"_s;
3739
3740 m_body += initialization + u";\n"_s;
3741 generateExceptionCheck();
3742 if (!resultPreparation.isEmpty())
3743 m_body += resultPreparation + u";\n"_s;
3744 m_body += u"}\n"_s;
3745}
3746
3747void QQmlJSCodeGenerator::generateJumpCodeWithTypeConversions(int relativeOffset)
3748{
3749 QString conversionCode;
3750 const int absoluteOffset = nextInstructionOffset() + relativeOffset;
3751 const auto annotation = m_annotations.find(key: absoluteOffset);
3752 if (static_cast<InstructionAnnotations::const_iterator>(annotation) != m_annotations.constEnd()) {
3753 const auto &conversions = annotation->second.typeConversions;
3754
3755 for (auto regIt = conversions.constBegin(), regEnd = conversions.constEnd();
3756 regIt != regEnd; ++regIt) {
3757 const QQmlJSRegisterContent targetType = regIt.value().content;
3758 if (!targetType.isValid() || !isTypeStorable(resolver: m_typeResolver, type: targetType.storedType()))
3759 continue;
3760
3761 const int registerIndex = regIt.key();
3762 const auto variable = m_registerVariables.constFind(key: RegisterVariablesKey {
3763 .internalName: targetType.storedType()->internalName(),
3764 .registerIndex: registerIndex,
3765 .lookupIndex: targetType.resultLookupIndex()
3766 });
3767
3768 if (variable == m_registerVariables.constEnd())
3769 continue;
3770
3771 QQmlJSRegisterContent currentType;
3772 QString currentVariable;
3773 if (registerIndex == m_state.changedRegisterIndex()) {
3774 currentVariable = changedRegisterVariable();
3775 if (variable->variableName == currentVariable)
3776 continue;
3777
3778 currentType = m_state.changedRegister();
3779 currentVariable = u"std::move("_s + currentVariable + u')';
3780 } else {
3781 const auto it = m_state.registers.find(key: registerIndex);
3782 if (it == m_state.registers.end()
3783 || variable->variableName == registerVariable(index: registerIndex)) {
3784 continue;
3785 }
3786
3787 currentType = it.value().content;
3788 currentVariable = consumedRegisterVariable(index: registerIndex);
3789 }
3790
3791 // Actually == here. We want the jump code also for equal types
3792 if (currentType == targetType)
3793 continue;
3794
3795 conversionCode += variable->variableName;
3796 conversionCode += u" = "_s;
3797 conversionCode += conversion(from: currentType, to: targetType, variable: currentVariable);
3798 conversionCode += u";\n"_s;
3799 }
3800 }
3801
3802 if (relativeOffset) {
3803 auto labelIt = m_labels.find(key: absoluteOffset);
3804 if (labelIt == m_labels.end())
3805 labelIt = m_labels.insert(key: absoluteOffset, value: u"label_%1"_s.arg(a: m_labels.size()));
3806 conversionCode += u" goto "_s + *labelIt + u";\n"_s;
3807 }
3808
3809 m_body += u"{\n"_s + conversionCode + u"}\n"_s;
3810}
3811
3812QString QQmlJSCodeGenerator::registerVariable(int index) const
3813{
3814 const QQmlJSRegisterContent &content = registerType(index);
3815 const auto it = m_registerVariables.constFind(key: RegisterVariablesKey {
3816 .internalName: content.storedType()->internalName(),
3817 .registerIndex: index,
3818 .lookupIndex: content.resultLookupIndex()
3819 });
3820 if (it != m_registerVariables.constEnd())
3821 return it->variableName;
3822
3823 return QString();
3824}
3825
3826QString QQmlJSCodeGenerator::lookupVariable(int lookupIndex) const
3827{
3828 for (auto it = m_registerVariables.constBegin(), end = m_registerVariables.constEnd(); it != end; ++it) {
3829 if (it.key().lookupIndex == lookupIndex)
3830 return it->variableName;
3831 }
3832 return QString();
3833}
3834
3835QString QQmlJSCodeGenerator::consumedRegisterVariable(int index) const
3836{
3837 const QString var = registerVariable(index);
3838 if (var.isEmpty() || !shouldMoveRegister(index))
3839 return var;
3840 return u"std::move(" + var + u")";
3841}
3842
3843QString QQmlJSCodeGenerator::consumedAccumulatorVariableIn() const
3844{
3845 return shouldMoveRegister(index: Accumulator)
3846 ? u"std::move(" + m_state.accumulatorVariableIn + u")"
3847 : m_state.accumulatorVariableIn;
3848}
3849
3850QString QQmlJSCodeGenerator::changedRegisterVariable() const
3851{
3852 const QQmlJSRegisterContent &changedRegister = m_state.changedRegister();
3853
3854 const QQmlJSScope::ConstPtr storedType = changedRegister.storedType();
3855 if (storedType.isNull())
3856 return QString();
3857
3858 return m_registerVariables.value(key: RegisterVariablesKey {
3859 .internalName: storedType->internalName(),
3860 .registerIndex: m_state.changedRegisterIndex(),
3861 .lookupIndex: changedRegister.resultLookupIndex()
3862 }).variableName;
3863}
3864
3865QQmlJSRegisterContent QQmlJSCodeGenerator::registerType(int index) const
3866{
3867 auto it = m_state.registers.find(key: index);
3868 if (it != m_state.registers.end())
3869 return it.value().content;
3870
3871 return QQmlJSRegisterContent();
3872}
3873
3874QQmlJSRegisterContent QQmlJSCodeGenerator::lookupType(int lookupIndex) const
3875{
3876 auto it = m_state.lookups.find(key: lookupIndex);
3877 if (it != m_state.lookups.end())
3878 return it.value().content;
3879
3880 return QQmlJSRegisterContent();
3881}
3882
3883bool QQmlJSCodeGenerator::shouldMoveRegister(int index) const
3884{
3885 return m_state.canMoveReadRegister(registerIndex: index)
3886 && !m_typeResolver->isTriviallyCopyable(type: m_state.readRegister(registerIndex: index).storedType());
3887}
3888
3889QString QQmlJSCodeGenerator::conversion(
3890 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
3891{
3892 const QQmlJSScope::ConstPtr contained = m_typeResolver->containedType(container: to);
3893
3894 // If from is QJSPrimitiveValue and to contains a primitive we coerce using QJSPrimitiveValue
3895 if (m_typeResolver->registerIsStoredIn(reg: from, type: m_typeResolver->jsPrimitiveType())
3896 && m_typeResolver->isPrimitive(type: to)) {
3897
3898 QString primitive = [&]() {
3899 if (m_typeResolver->equals(a: contained, b: m_typeResolver->jsPrimitiveType()))
3900 return variable;
3901
3902 const QString conversion = variable + u".to<QJSPrimitiveValue::%1>()"_s;
3903 if (m_typeResolver->equals(a: contained, b: m_typeResolver->boolType()))
3904 return conversion.arg(a: u"Boolean"_s);
3905 if (m_typeResolver->isIntegral(type: to))
3906 return conversion.arg(a: u"Integer"_s);
3907 if (m_typeResolver->isNumeric(type: to))
3908 return conversion.arg(a: u"Double"_s);
3909 if (m_typeResolver->equals(a: contained, b: m_typeResolver->stringType()))
3910 return conversion.arg(a: u"String"_s);
3911 reject(thing: u"Conversion of QJSPrimitiveValue to "_s + contained->internalName());
3912 return QString();
3913 }();
3914
3915 if (primitive.isEmpty())
3916 return primitive;
3917
3918 return convertStored(from: m_typeResolver->jsPrimitiveType(), to: to.storedType(), variable: primitive);
3919 }
3920
3921 if (m_typeResolver->registerIsStoredIn(reg: to, type: contained)
3922 || m_typeResolver->isNumeric(type: to.storedType())
3923 || to.storedType()->isReferenceType()
3924 || m_typeResolver->registerContains(reg: from, type: contained)) {
3925 // If:
3926 // * the output is not actually wrapped at all, or
3927 // * the output is stored in a numeric type (as there are no internals to a number), or
3928 // * the output is a QObject pointer, or
3929 // * we merely wrap the value into a new container,
3930 // we can convert by stored type.
3931 return convertStored(from: from.storedType(), to: to.storedType(), variable);
3932 } else {
3933 return convertContained(from, to, variable);
3934 }
3935}
3936
3937QString QQmlJSCodeGenerator::convertStored(
3938 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to, const QString &variable)
3939{
3940 // TODO: most values can be moved, which is much more efficient with the common types.
3941 // add a move(from, to, variable) function that implements the moves.
3942 Q_ASSERT(!to->isComposite()); // We cannot directly convert to composites.
3943
3944 const auto jsValueType = m_typeResolver->jsValueType();
3945 const auto varType = m_typeResolver->varType();
3946 const auto jsPrimitiveType = m_typeResolver->jsPrimitiveType();
3947 const auto boolType = m_typeResolver->boolType();
3948
3949 auto zeroBoolOrInt = [&](const QQmlJSScope::ConstPtr &to) {
3950 if (m_typeResolver->equals(a: to, b: boolType))
3951 return u"false"_s;
3952 if (m_typeResolver->isSignedInteger(type: to))
3953 return u"0"_s;
3954 if (m_typeResolver->isUnsignedInteger(type: to))
3955 return u"0u"_s;
3956 return QString();
3957 };
3958
3959 if (m_typeResolver->equals(a: from, b: m_typeResolver->voidType())) {
3960 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3961 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3962 const QString zero = zeroBoolOrInt(to);
3963 if (!zero.isEmpty())
3964 return zero;
3965 if (m_typeResolver->equals(a: to, b: m_typeResolver->floatType()))
3966 return u"std::numeric_limits<float>::quiet_NaN()"_s;
3967 if (m_typeResolver->equals(a: to, b: m_typeResolver->realType()))
3968 return u"std::numeric_limits<double>::quiet_NaN()"_s;
3969 if (m_typeResolver->equals(a: to, b: m_typeResolver->stringType()))
3970 return QQmlJSUtils::toLiteral(s: u"undefined"_s);
3971 if (m_typeResolver->equals(a: to, b: m_typeResolver->varType()))
3972 return u"QVariant()"_s;
3973 if (m_typeResolver->equals(a: to, b: m_typeResolver->jsValueType()))
3974 return u"QJSValue();"_s;
3975 if (m_typeResolver->equals(a: to, b: m_typeResolver->jsPrimitiveType()))
3976 return u"QJSPrimitiveValue()"_s;
3977 if (m_typeResolver->equals(a: from, b: to))
3978 return QString();
3979 }
3980
3981 if (m_typeResolver->equals(a: from, b: m_typeResolver->nullType())) {
3982 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
3983 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
3984 if (m_typeResolver->equals(a: to, b: jsValueType))
3985 return u"QJSValue(QJSValue::NullValue)"_s;
3986 if (m_typeResolver->equals(a: to, b: jsPrimitiveType))
3987 return u"QJSPrimitiveValue(QJSPrimitiveNull())"_s;
3988 if (m_typeResolver->equals(a: to, b: varType))
3989 return u"QVariant::fromValue<std::nullptr_t>(nullptr)"_s;
3990 const QString zero = zeroBoolOrInt(to);
3991 if (!zero.isEmpty())
3992 return zero;
3993 if (m_typeResolver->equals(a: to, b: m_typeResolver->floatType()))
3994 return u"0.0f"_s;
3995 if (m_typeResolver->equals(a: to, b: m_typeResolver->realType()))
3996 return u"0.0"_s;
3997 if (m_typeResolver->equals(a: to, b: m_typeResolver->stringType()))
3998 return QQmlJSUtils::toLiteral(s: u"null"_s);
3999 if (m_typeResolver->equals(a: from, b: to))
4000 return QString();
4001 reject(thing: u"Conversion from null to %1"_s.arg(a: to->internalName()));
4002 }
4003
4004 if (m_typeResolver->equals(a: from, b: to))
4005 return variable;
4006
4007 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4008 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
4009 // Compare internalName here. The same C++ type can be exposed muliple times in
4010 // different QML types. However, the C++ names have to be unique. We can always
4011 // static_cast to those.
4012
4013 for (QQmlJSScope::ConstPtr base = from; base; base = base->baseType()) {
4014 // We still have to cast as other execution paths may result in different types.
4015 if (base->internalName() == to->internalName())
4016 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4017 }
4018 for (QQmlJSScope::ConstPtr base = to; base; base = base->baseType()) {
4019 if (base->internalName() == from->internalName())
4020 return u"static_cast<"_s + to->internalName() + u" *>("_s + variable + u')';
4021 }
4022 } else if (m_typeResolver->equals(a: to, b: m_typeResolver->boolType())) {
4023 return u'(' + variable + u" != nullptr)"_s;
4024 }
4025 }
4026
4027 auto isJsValue = [&](const QQmlJSScope::ConstPtr &candidate) {
4028 return m_typeResolver->equals(a: candidate, b: jsValueType) || candidate->isScript();
4029 };
4030
4031 if (isJsValue(from) && isJsValue(to))
4032 return variable;
4033
4034 const auto isBoolOrNumber = [&](const QQmlJSScope::ConstPtr &type) {
4035 return m_typeResolver->isNumeric(type: m_typeResolver->globalType(type))
4036 || m_typeResolver->equals(a: type, b: m_typeResolver->boolType())
4037 || type->scopeType() == QQmlSA::ScopeType::EnumScope;
4038 };
4039
4040 if (m_typeResolver->equals(a: from, b: m_typeResolver->realType())
4041 || m_typeResolver->equals(a: from, b: m_typeResolver->floatType())) {
4042 if (m_typeResolver->isSignedInteger(type: to))
4043 return u"QJSNumberCoercion::toInteger("_s + variable + u')';
4044 if (m_typeResolver->isUnsignedInteger(type: to))
4045 return u"uint(QJSNumberCoercion::toInteger("_s + variable + u"))"_s;
4046 if (m_typeResolver->equals(a: to, b: m_typeResolver->boolType()))
4047 return u"[](double moved){ return moved && !std::isnan(moved); }("_s + variable + u')';
4048 }
4049
4050 if (isBoolOrNumber(from) && isBoolOrNumber(to))
4051 return to->internalName() + u'(' + variable + u')';
4052
4053
4054 if (m_typeResolver->equals(a: from, b: jsPrimitiveType)) {
4055 if (m_typeResolver->equals(a: to, b: m_typeResolver->realType()))
4056 return variable + u".toDouble()"_s;
4057 if (m_typeResolver->equals(a: to, b: boolType))
4058 return variable + u".toBoolean()"_s;
4059 if (m_typeResolver->equals(a: to, b: m_typeResolver->int64Type())
4060 || m_typeResolver->equals(a: to, b: m_typeResolver->uint64Type())) {
4061 return u"%1(%2.toDouble())"_s.arg(args: to->internalName(), args: variable);
4062 }
4063 if (m_typeResolver->isIntegral(type: to))
4064 return u"%1(%2.toInteger())"_s.arg(args: to->internalName(), args: variable);
4065 if (m_typeResolver->equals(a: to, b: m_typeResolver->stringType()))
4066 return variable + u".toString()"_s;
4067 if (m_typeResolver->equals(a: to, b: jsValueType))
4068 return u"QJSValue(QJSPrimitiveValue("_s + variable + u"))"_s;
4069 if (m_typeResolver->equals(a: to, b: varType))
4070 return variable + u".toVariant()"_s;
4071 if (to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
4072 return u"static_cast<"_s + to->internalName() + u" *>(nullptr)"_s;
4073 }
4074
4075 if (isJsValue(from)) {
4076 if (m_typeResolver->equals(a: to, b: jsPrimitiveType))
4077 return variable + u".toPrimitive()"_s;
4078 if (m_typeResolver->equals(a: to, b: varType))
4079 return variable + u".toVariant(QJSValue::RetainJSObjects)"_s;
4080 return u"qjsvalue_cast<"_s + castTargetName(type: to) + u">("_s + variable + u')';
4081 }
4082
4083 if (m_typeResolver->equals(a: to, b: jsPrimitiveType)) {
4084 // null and undefined have been handled above already
4085 Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->nullType()));
4086 Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
4087
4088 if (m_typeResolver->equals(a: from, b: m_typeResolver->boolType())
4089 || m_typeResolver->equals(a: from, b: m_typeResolver->int32Type())
4090 || m_typeResolver->equals(a: from, b: m_typeResolver->realType())
4091 || m_typeResolver->equals(a: from, b: m_typeResolver->stringType())) {
4092 return u"QJSPrimitiveValue("_s + variable + u')';
4093 } else if (m_typeResolver->equals(a: from, b: m_typeResolver->int16Type())
4094 || m_typeResolver->equals(a: from, b: m_typeResolver->int8Type())
4095 || m_typeResolver->equals(a: from, b: m_typeResolver->uint16Type())
4096 || m_typeResolver->equals(a: from, b: m_typeResolver->uint8Type())) {
4097 return u"QJSPrimitiveValue(int("_s + variable + u"))"_s;
4098 } else if (m_typeResolver->isNumeric(type: from)) {
4099 return u"QJSPrimitiveValue(double("_s + variable + u"))"_s;
4100 }
4101 }
4102
4103 if (m_typeResolver->equals(a: to, b: jsValueType))
4104 return u"aotContext->engine->toScriptValue("_s + variable + u')';
4105
4106 if (m_typeResolver->equals(a: from, b: varType)) {
4107 if (m_typeResolver->equals(a: to, b: m_typeResolver->listPropertyType()))
4108 return u"QQmlListReference("_s + variable + u", aotContext->qmlEngine())"_s;
4109 return u"aotContext->engine->fromVariant<"_s + castTargetName(type: to) + u">("_s
4110 + variable + u')';
4111 }
4112
4113 if (m_typeResolver->equals(a: to, b: varType))
4114 return u"QVariant::fromValue("_s + variable + u')';
4115
4116 if (m_typeResolver->equals(a: from, b: m_typeResolver->urlType())
4117 && m_typeResolver->equals(a: to, b: m_typeResolver->stringType())) {
4118 return variable + u".toString()"_s;
4119 }
4120
4121 if (m_typeResolver->equals(a: from, b: m_typeResolver->stringType())
4122 && m_typeResolver->equals(a: to, b: m_typeResolver->urlType())) {
4123 return u"QUrl("_s + variable + u')';
4124 }
4125
4126 if (m_typeResolver->equals(a: from, b: m_typeResolver->byteArrayType())
4127 && m_typeResolver->equals(a: to, b: m_typeResolver->stringType())) {
4128 return u"QString::fromUtf8("_s + variable + u')';
4129 }
4130
4131 if (m_typeResolver->equals(a: from, b: m_typeResolver->stringType())
4132 && m_typeResolver->equals(a: to, b: m_typeResolver->byteArrayType())) {
4133 return variable + u".toUtf8()"_s;
4134 }
4135
4136 for (const auto &originType : {
4137 m_typeResolver->dateTimeType(),
4138 m_typeResolver->dateType(),
4139 m_typeResolver->timeType()}) {
4140 if (m_typeResolver->equals(a: from, b: originType)) {
4141 for (const auto &targetType : {
4142 m_typeResolver->dateTimeType(),
4143 m_typeResolver->dateType(),
4144 m_typeResolver->timeType(),
4145 m_typeResolver->stringType(),
4146 m_typeResolver->realType()}) {
4147 if (m_typeResolver->equals(a: to, b: targetType)) {
4148 return u"aotContext->engine->coerceValue<%1, %2>(%3)"_s.arg(
4149 args: originType->internalName(), args: targetType->internalName(), args: variable);
4150 }
4151 }
4152 break;
4153 }
4154 }
4155
4156 const auto retrieveFromPrimitive = [&](
4157 const QQmlJSScope::ConstPtr &type, const QString &expression) -> QString
4158 {
4159 if (m_typeResolver->equals(a: type, b: m_typeResolver->boolType()))
4160 return expression + u".toBoolean()"_s;
4161 if (m_typeResolver->isSignedInteger(type))
4162 return expression + u".toInteger()"_s;
4163 if (m_typeResolver->isUnsignedInteger(type))
4164 return u"uint("_s + expression + u".toInteger())"_s;
4165 if (m_typeResolver->equals(a: type, b: m_typeResolver->realType()))
4166 return expression + u".toDouble()"_s;
4167 if (m_typeResolver->equals(a: type, b: m_typeResolver->floatType()))
4168 return u"float("_s + expression + u".toDouble())"_s;
4169 if (m_typeResolver->equals(a: type, b: m_typeResolver->stringType()))
4170 return expression + u".toString()"_s;
4171 return QString();
4172 };
4173
4174 if (!retrieveFromPrimitive(from, u"x"_s).isEmpty()) {
4175 const QString retrieve = retrieveFromPrimitive(
4176 to, convertStored(from, to: m_typeResolver->jsPrimitiveType(), variable));
4177 if (!retrieve.isEmpty())
4178 return retrieve;
4179 }
4180
4181 if (from->isReferenceType() && m_typeResolver->equals(a: to, b: m_typeResolver->stringType())) {
4182 return u"aotContext->engine->coerceValue<"_s + castTargetName(type: from) + u", "
4183 + castTargetName(type: to) + u">("_s + variable + u')';
4184 }
4185
4186 // Any value type is a non-null JS 'object' and therefore coerces to true.
4187 if (m_typeResolver->equals(a: to, b: m_typeResolver->boolType())) {
4188 // All the interesting cases are already handled above:
4189 Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->nullType()));
4190 Q_ASSERT(!m_typeResolver->equals(from, m_typeResolver->voidType()));
4191 Q_ASSERT(retrieveFromPrimitive(from, u"x"_s).isEmpty());
4192 Q_ASSERT(!isBoolOrNumber(from));
4193
4194 return u"true"_s;
4195 }
4196
4197 if (m_typeResolver->areEquivalentLists(a: from, b: to))
4198 return variable;
4199
4200 if (from->isListProperty()
4201 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
4202 && to->valueType()->isReferenceType()
4203 && !to->isListProperty()) {
4204 return variable + u".toList<"_s + to->internalName() + u">()"_s;
4205 }
4206
4207 bool isExtension = false;
4208 if (m_typeResolver->canPopulate(type: to, argument: from, isExtension: &isExtension)) {
4209 reject(thing: u"populating "_s + to->internalName() + u" from "_s + from->internalName());
4210 } else if (const auto ctor = m_typeResolver->selectConstructor(type: to, argument: from, isExtension: &isExtension);
4211 ctor.isValid()) {
4212 const auto argumentTypes = ctor.parameters();
4213 return (isExtension ? to->extensionType().scope->internalName() : to->internalName())
4214 + u"("_s + convertStored(from, to: argumentTypes[0].type(), variable) + u")"_s;
4215 }
4216
4217 if (m_typeResolver->equals(a: to, b: m_typeResolver->stringType())
4218 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
4219 addInclude(include: u"QtQml/qjslist.h"_s);
4220
4221 // Extend the life time of whatever variable is across the call to toString().
4222 // variable may be an rvalue.
4223 return u"[&](auto &&l){ return QJSList(&l, aotContext->engine).toString(); }("_s
4224 + variable + u')';
4225 }
4226
4227 // TODO: add more conversions
4228
4229 reject(thing: u"conversion from "_s + from->internalName() + u" to "_s + to->internalName());
4230 return QString();
4231}
4232
4233QString QQmlJSCodeGenerator::convertContained(const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to, const QString &variable)
4234{
4235 const QQmlJSScope::ConstPtr containedFrom = m_typeResolver->containedType(container: from);
4236 const QQmlJSScope::ConstPtr containedTo = m_typeResolver->containedType(container: to);
4237
4238 // Those should be handled before, by convertStored().
4239 Q_ASSERT(!to.storedType()->isReferenceType());
4240 Q_ASSERT(!m_typeResolver->registerIsStoredIn(to, containedTo));
4241 Q_ASSERT(!m_typeResolver->isIntegral(from.storedType()));
4242 Q_ASSERT(!m_typeResolver->equals(containedFrom, containedTo));
4243
4244 if (!m_typeResolver->registerIsStoredIn(reg: to, type: m_typeResolver->varType()) &&
4245 !m_typeResolver->registerIsStoredIn(reg: to, type: m_typeResolver->jsPrimitiveType())) {
4246 reject(thing: u"internal conversion into unsupported wrapper type."_s);
4247 return QString();
4248 }
4249
4250 bool isExtension = false;
4251 if (m_typeResolver->canPopulate(type: containedTo, argument: containedFrom, isExtension: &isExtension)) {
4252 reject(thing: u"populating "_s + containedTo->internalName()
4253 + u" from "_s + containedFrom->internalName());
4254 return QString();
4255 } else if (const auto ctor = m_typeResolver->selectConstructor(
4256 type: containedTo, argument: containedFrom, isExtension: &isExtension); ctor.isValid()) {
4257 const auto argumentTypes = ctor.parameters();
4258 const QQmlJSScope::ConstPtr argumentType = argumentTypes[0].type();
4259
4260 // We need to store the converted argument in a temporary
4261 // because it might not be an lvalue.
4262
4263 QString input;
4264 QString argPointer;
4265
4266 if (m_typeResolver->equals(a: argumentType, b: containedFrom)) {
4267 input = variable;
4268 argPointer = contentPointer(content: from, var: u"arg"_s);
4269 } else {
4270 const QQmlJSRegisterContent argument
4271 = m_typeResolver->globalType(type: argumentType)
4272 .storedIn(newStoredType: m_typeResolver->genericType(type: argumentType));
4273 input = conversion(from, to: argument, variable);
4274 argPointer = contentPointer(content: argument, var: u"arg"_s);
4275 }
4276
4277 return u"[&](){ auto arg = " + input
4278 + u"; return aotContext->constructValueType("_s + metaType(type: containedTo)
4279 + u", "_s + metaObject(
4280 objectType: isExtension ? containedTo->extensionType().scope : containedTo)
4281 + u", "_s + QString::number(int(ctor.constructorIndex()))
4282 + u", "_s + argPointer + u"); }()"_s;
4283 }
4284
4285 const auto originalFrom = m_typeResolver->original(type: from);
4286 const auto containedOriginalFrom = m_typeResolver->containedType(container: originalFrom);
4287 if (!m_typeResolver->equals(a: containedFrom, b: containedOriginalFrom)
4288 && m_typeResolver->canHold(container: containedFrom, contained: containedOriginalFrom)) {
4289 // If from is simply a wrapping of a specific type into a more general one, we can convert
4290 // the original type instead. You can't nest wrappings after all.
4291 return conversion(from: originalFrom.storedIn(newStoredType: from.storedType()), to, variable);
4292 }
4293
4294 if (m_typeResolver->isPrimitive(type: containedFrom) && m_typeResolver->isPrimitive(type: containedTo)) {
4295 const QQmlJSRegisterContent intermediate = from.storedIn(newStoredType: m_typeResolver->jsPrimitiveType());
4296 return conversion(from: intermediate, to, variable: conversion(from, to: intermediate, variable));
4297 }
4298
4299 reject(thing: u"internal conversion with incompatible or ambiguous types: %1 -> %2"_s
4300 .arg(args: from.descriptiveName(), args: to.descriptiveName()));
4301 return QString();
4302}
4303
4304void QQmlJSCodeGenerator::reject(const QString &thing)
4305{
4306 setError(u"Cannot generate efficient code for %1"_s.arg(a: thing));
4307}
4308
4309QQmlJSCodeGenerator::AccumulatorConverter::AccumulatorConverter(QQmlJSCodeGenerator *generator)
4310 : accumulatorOut(generator->m_state.accumulatorOut())
4311 , accumulatorVariableIn(generator->m_state.accumulatorVariableIn)
4312 , accumulatorVariableOut(generator->m_state.accumulatorVariableOut)
4313 , generator(generator)
4314{
4315 if (accumulatorVariableOut.isEmpty())
4316 return;
4317
4318 const QQmlJSTypeResolver *resolver = generator->m_typeResolver;
4319 const QQmlJSScope::ConstPtr origContained = resolver->originalContainedType(container: accumulatorOut);
4320 const QQmlJSScope::ConstPtr stored = accumulatorOut.storedType();
4321 const QQmlJSScope::ConstPtr origStored = resolver->originalType(type: stored);
4322
4323 // If the stored type differs or if we store in QVariant and the contained type differs,
4324 // then we have to use a temporary ...
4325 if (!resolver->equals(a: origStored, b: stored)
4326 || (!resolver->equals(a: origContained, b: resolver->containedType(container: accumulatorOut))
4327 && resolver->equals(a: stored, b: resolver->varType()))) {
4328
4329 const bool storable = isTypeStorable(resolver, type: origStored);
4330 generator->m_state.accumulatorVariableOut = storable ? u"retrieved"_s : QString();
4331 generator->m_state.setRegister(registerIndex: Accumulator, content: resolver->original(type: accumulatorOut));
4332 generator->m_body += u"{\n"_s;
4333 if (storable) {
4334 generator->m_body += origStored->augmentedInternalName() + u' '
4335 + generator->m_state.accumulatorVariableOut + u";\n";
4336 }
4337 } else if (generator->m_state.accumulatorVariableIn == generator->m_state.accumulatorVariableOut
4338 && generator->m_state.readsRegister(registerIndex: Accumulator)
4339 && resolver->registerIsStoredIn(
4340 reg: generator->m_state.accumulatorOut(), type: resolver->varType())) {
4341 // If both m_state.accumulatorIn and m_state.accumulatorOut are QVariant, we will need to
4342 // prepare the output QVariant, and afterwards use the input variant. Therefore we need to
4343 // move the input out of the way first.
4344 generator->m_state.accumulatorVariableIn
4345 = generator->m_state.accumulatorVariableIn + u"_moved"_s;
4346 generator->m_body += u"{\n"_s;
4347 generator->m_body += u"QVariant "_s + generator->m_state.accumulatorVariableIn
4348 + u" = std::move("_s + generator->m_state.accumulatorVariableOut + u");\n"_s;
4349 }
4350}
4351
4352QQmlJSCodeGenerator::AccumulatorConverter::~AccumulatorConverter()
4353{
4354 if (accumulatorVariableOut != generator->m_state.accumulatorVariableOut) {
4355 generator->m_body += accumulatorVariableOut + u" = "_s + generator->conversion(
4356 from: generator->m_state.accumulatorOut(), to: accumulatorOut,
4357 variable: u"std::move("_s + generator->m_state.accumulatorVariableOut + u')') + u";\n"_s;
4358 generator->m_body += u"}\n"_s;
4359 generator->m_state.setRegister(registerIndex: Accumulator, content: accumulatorOut);
4360 generator->m_state.accumulatorVariableOut = accumulatorVariableOut;
4361 } else if (accumulatorVariableIn != generator->m_state.accumulatorVariableIn) {
4362 generator->m_body += u"}\n"_s;
4363 generator->m_state.accumulatorVariableIn = accumulatorVariableIn;
4364 }
4365}
4366
4367
4368QT_END_NAMESPACE
4369

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qmlcompiler/qqmljscodegenerator.cpp