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