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

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