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