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