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
qv4codegen.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4codegen_p.h"
5
6#include <private/qqmljsast_p.h>
7#include <private/qqmljsdiagnosticmessage_p.h>
8#include <private/qqmljslexer_p.h>
9#include <private/qqmljsparser_p.h>
10#include <private/qv4bytecodegenerator_p.h>
11#include <private/qv4compilercontext_p.h>
12#include <private/qv4compilercontrolflow_p.h>
13#include <private/qv4compilerscanfunctions_p.h>
14#include <private/qv4object_p.h>
15#include <private/qv4objectiterator_p.h>
16#include <private/qv4staticvalue_p.h>
17#include <private/qv4stringtoarrayindex_p.h>
18
19#include <QtCore/qcoreapplication.h>
20#include <QtCore/qloggingcategory.h>
21#include <QtCore/qscopeguard.h>
22#include <QtCore/qstack.h>
23#include <QtCore/qstringlist.h>
24#include <QtCore/qurl.h>
25
26#include <cmath>
27
28#ifdef CONST
29#undef CONST
30#endif
31
32QT_BEGIN_NAMESPACE
33
34using namespace Qt::StringLiterals;
35
36Q_STATIC_LOGGING_CATEGORY(lcQmlUsedBeforeDeclared, "qt.qml.usedbeforedeclared");
37Q_STATIC_LOGGING_CATEGORY(lcQmlInjectedParameter, "qt.qml.injectedparameter");
38
39using namespace QV4;
40using namespace QV4::Compiler;
41using namespace QQmlJS;
42using namespace QQmlJS::AST;
43
44void CodegenWarningInterface::reportVarUsedBeforeDeclaration(
45 const QString &name, const QString &fileName, QQmlJS::SourceLocation declarationLocation,
46 QQmlJS::SourceLocation accessLocation)
47{
48 qCWarning(lcQmlUsedBeforeDeclared).nospace().noquote()
49 << fileName << ":" << accessLocation.startLine << ":" << accessLocation.startColumn
50 << " Variable \"" << name << "\" is used before its declaration at "
51 << declarationLocation.startLine << ":" << declarationLocation.startColumn << ".";
52}
53
54void CodegenWarningInterface::reportFunctionUsedBeforeDeclaration(const QString &, const QString &,
55 QQmlJS::SourceLocation,
56 QQmlJS::SourceLocation)
57{
58 // we don't report this by default, only when using qmllint.
59}
60
61static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator,
62 const Statement *body, const SourceLocation &fallback)
63{
64 switch (body->kind) {
65 // Statements where we might never execute the last line.
66 // Use the fallback.
67 case Statement::Kind_ConditionalExpression:
68 case Statement::Kind_ForEachStatement:
69 case Statement::Kind_ForStatement:
70 case Statement::Kind_IfStatement:
71 case Statement::Kind_WhileStatement:
72 bytecodeGenerator->setLocation(fallback);
73 break;
74 default:
75 bytecodeGenerator->setLocation(body->lastSourceLocation());
76 break;
77 }
78}
79
80void Codegen::generateThrowException(const QString &type, const QString &text)
81{
82 RegisterScope scope(this);
83 Instruction::Construct construct;
84 if (text.isEmpty()) {
85 construct.argc = 0;
86 construct.argv = 0;
87 } else {
88 construct.argc = 1;
89 Instruction::LoadRuntimeString load;
90 load.stringId = registerString(text);
91 bytecodeGenerator->addInstruction(load);
92 construct.argv = Reference::fromAccumulator(this).storeOnStack().stackSlot();
93 }
94 Reference r = referenceForName(type, false);
95 r = r.storeOnStack();
96 construct.func = r.stackSlot();
97 bytecodeGenerator->addInstruction(construct);
98 Instruction::ThrowException throwException;
99 bytecodeGenerator->addInstruction(throwException);
100}
101
102Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict,
103 CodegenWarningInterface *iface, bool storeSourceLocations)
104 : _module(nullptr),
105 _returnAddress(-1),
106 _context(nullptr),
107 _labelledStatement(nullptr),
108 jsUnitGenerator(jsUnitGenerator),
109 _strictMode(strict),
110 storeSourceLocations(storeSourceLocations),
111 _fileNameIsUrl(false),
112 _interface(iface)
113{
114 jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
115 pushExpr();
116}
117
118static constexpr const QLatin1StringView s_globalNames[] = {
119 QLatin1StringView("Array"),
120 QLatin1StringView("ArrayBuffer"),
121 QLatin1StringView("Atomics"),
122 QLatin1StringView("Boolean"),
123 QLatin1StringView("DOMException"),
124 QLatin1StringView("DataView"),
125 QLatin1StringView("Date"),
126 QLatin1StringView("Error"),
127 QLatin1StringView("EvalError"),
128 QLatin1StringView("Float32Array"),
129 QLatin1StringView("Float64Array"),
130 QLatin1StringView("Function"),
131 QLatin1StringView("Infinity"),
132 QLatin1StringView("Int16Array"),
133 QLatin1StringView("Int32Array"),
134 QLatin1StringView("Int8Array"),
135 QLatin1StringView("JSON"),
136 QLatin1StringView("Map"),
137 QLatin1StringView("Math"),
138 QLatin1StringView("NaN"),
139 QLatin1StringView("Number"),
140 QLatin1StringView("Object"),
141 QLatin1StringView("Promise"),
142 QLatin1StringView("Proxy"),
143 QLatin1StringView("QT_TRANSLATE_NOOP"),
144 QLatin1StringView("QT_TRID_NOOP"),
145 QLatin1StringView("QT_TR_NOOP"),
146 QLatin1StringView("Qt"),
147 QLatin1StringView("RangeError"),
148 QLatin1StringView("ReferenceError"),
149 QLatin1StringView("Reflect"),
150 QLatin1StringView("RegExp"),
151 QLatin1StringView("SQLException"),
152 QLatin1StringView("Set"),
153 QLatin1StringView("SharedArrayBuffer"),
154 QLatin1StringView("String"),
155 QLatin1StringView("Symbol"),
156 QLatin1StringView("SyntaxError"),
157 QLatin1StringView("TypeError"),
158 QLatin1StringView("URIError"),
159 QLatin1StringView("URL"),
160 QLatin1StringView("URLSearchParams"),
161 QLatin1StringView("Uint16Array"),
162 QLatin1StringView("Uint32Array"),
163 QLatin1StringView("Uint8Array"),
164 QLatin1StringView("Uint8ClampedArray"),
165 QLatin1StringView("WeakMap"),
166 QLatin1StringView("WeakSet"),
167 QLatin1StringView("XMLHttpRequest"),
168 QLatin1StringView("console"),
169 QLatin1StringView("decodeURI"),
170 QLatin1StringView("decodeURIComponent"),
171 QLatin1StringView("encodeURI"),
172 QLatin1StringView("encodeURIComponent"),
173 QLatin1StringView("escape"),
174 QLatin1StringView("eval"),
175 QLatin1StringView("gc"),
176 QLatin1StringView("isFinite"),
177 QLatin1StringView("isNaN"),
178 QLatin1StringView("parseFloat"),
179 QLatin1StringView("parseInt"),
180 QLatin1StringView("print"),
181 QLatin1StringView("qsTr"),
182 QLatin1StringView("qsTrId"),
183 QLatin1StringView("qsTranslate"),
184 QLatin1StringView("undefined"),
185 QLatin1StringView("unescape"),
186};
187
188bool Codegen::isNameGlobal(QAnyStringView name)
189{
190 return std::binary_search(std::begin(s_globalNames), std::end(s_globalNames), name);
191}
192
193void Codegen::forEachGlobalName(qxp::function_ref<void (QLatin1StringView)> &&handler)
194{
195 for (QLatin1StringView name : s_globalNames)
196 handler(name);
197}
198
199void Codegen::generateFromProgram(
200 const QString &sourceCode, Program *node, Module *module, ContextType contextType)
201{
202 Q_ASSERT(node);
203
204 _module = module;
205 _context = nullptr;
206
207 ScanFunctions scan(this, sourceCode, contextType);
208 scan(node);
209
210 if (hasError())
211 return;
212
213 defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
214}
215
216void Codegen::generateFromModule(const QString &sourceCode, ESModule *node, Module *module)
217{
218 Q_ASSERT(node);
219
220 _module = module;
221 _context = nullptr;
222
223 ScanFunctions scan(this, sourceCode, ContextType::ESModule);
224 scan(node);
225
226 if (hasError())
227 return;
228
229 {
230 Compiler::Context *moduleContext = _module->contextMap.value(node);
231 for (const auto &entry: moduleContext->exportEntries) {
232 if (entry.moduleRequest.isEmpty()) {
233 // ### check against imported bound names
234 _module->localExportEntries << entry;
235 } else if (entry.importName == QLatin1Char('*')) {
236 _module->starExportEntries << entry;
237 } else {
238 _module->indirectExportEntries << entry;
239 }
240 }
241 _module->importEntries = moduleContext->importEntries;
242
243 _module->moduleRequests = std::move(moduleContext->moduleRequests);
244 _module->moduleRequests.removeDuplicates();
245 }
246
247 std::sort(_module->localExportEntries.begin(), _module->localExportEntries.end(), ExportEntry::lessThan);
248 std::sort(_module->starExportEntries.begin(), _module->starExportEntries.end(), ExportEntry::lessThan);
249 std::sort(_module->indirectExportEntries.begin(), _module->indirectExportEntries.end(), ExportEntry::lessThan);
250
251 defineFunction(QStringLiteral("%entry"), node, nullptr, node->body);
252}
253
254void Codegen::generateFromModule(const Value &value, Module *module)
255{
256 _module = module;
257 _context = nullptr;
258
259 _module->newContext(nullptr, nullptr, ContextType::ESModule);
260 enterContext(nullptr);
261
262 _context->name = QStringLiteral("%entry");
263 _module->functions.append(_context);
264 _context->functionIndex = _module->functions.size() - 1;
265
266 ExportEntry entry;
267 entry.localName = entry.exportName = QLatin1String("default");
268 _module->localExportEntries << entry;
269
270 if (Object *o = value.objectValue()) {
271 QV4::Scope scope(o);
272 QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
273 QV4::PropertyAttributes attrs;
274 QV4::ScopedPropertyKey name(scope);
275 while (true) {
276 name = it.next(nullptr, &attrs);
277 if (!name->isValid())
278 break;
279
280 ExportEntry entry;
281 entry.localName = entry.exportName = name->toQString();
282 _module->localExportEntries << entry;
283 }
284 }
285
286 std::sort(_module->localExportEntries.begin(), _module->localExportEntries.end(),
287 ExportEntry::lessThan);
288
289 for (auto it = _module->localExportEntries.cbegin(), end = _module->localExportEntries.cend();
290 it != end; ++it) {
291 Context::Member member;
292 member.index = _context->locals.size();
293 _context->locals.append(it->exportName);
294 _context->members.insert(it->exportName, member);
295 }
296
297 leaveContext();
298}
299
300void Codegen::enterContext(Node *node)
301{
302 _context = _module->contextMap.value(node);
303 Q_ASSERT(_context);
304}
305
306int Codegen::leaveContext()
307{
308 Q_ASSERT(_context);
309 int functionIndex = _context->functionIndex;
310 _context = _context->parent;
311 return functionIndex;
312}
313
314Context *Codegen::enterBlock(Node *node)
315{
316 enterContext(node);
317 return _context;
318}
319
320Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
321{
322 if (hasError())
323 return exprResult();
324
325 if (expr.isConstant()) {
326 auto v = StaticValue::fromReturnedValue(expr.constant);
327 if (v.isNumber()) {
328 switch (op) {
329 case Not:
330 return Reference::fromConst(this, Encode(!v.toBoolean()));
331 case UMinus:
332 // This duplicates some of the logic from Runtime::UMinus::call()
333 ReturnedValue r;
334 if (v.isInteger()) {
335 int intVal = v.integerValue();
336 if (intVal && intVal != std::numeric_limits<int>::min())
337 r = QV4::Encode(-intVal);
338 else
339 r = QV4::Encode(-double(intVal));
340 } else if (v.isDouble()) {
341 r = QV4::Encode(-v.doubleValue());
342 } else {
343 r = QV4::Encode(-v.int_32());
344 }
345 return Reference::fromConst(this, r);
346 case UPlus:
347 return expr;
348 case Compl:
349 return Reference::fromConst(this, Encode((int)~v.toInt32()));
350 default:
351 break;
352 }
353 }
354 }
355
356 switch (op) {
357 case UMinus: {
358 expr.loadInAccumulator();
359 Instruction::UMinus uminus = {};
360 bytecodeGenerator->addInstruction(uminus);
361 return Reference::fromAccumulator(this);
362 }
363 case UPlus: {
364 expr.loadInAccumulator();
365 Instruction::UPlus uplus = {};
366 bytecodeGenerator->addInstruction(uplus);
367 return Reference::fromAccumulator(this);
368 }
369 case Not: {
370 expr.loadInAccumulator();
371 Instruction::UNot unot;
372 bytecodeGenerator->addInstruction(unot);
373 return Reference::fromAccumulator(this);
374 }
375 case Compl: {
376 expr.loadInAccumulator();
377 Instruction::UCompl ucompl;
378 bytecodeGenerator->addInstruction(ucompl);
379 return Reference::fromAccumulator(this);
380 }
381 case PostIncrement:
382 if (!exprAccept(nx) || requiresReturnValue) {
383 Reference e = expr.asLValue();
384 e.loadInAccumulator();
385 Instruction::UPlus uplus = {};
386 bytecodeGenerator->addInstruction(uplus);
387 Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
388 Instruction::Increment inc = {};
389 bytecodeGenerator->addInstruction(inc);
390 e.storeConsumeAccumulator();
391 return originalValue;
392 } else {
393 // intentionally fall-through: the result is never used, so it's equivalent to
394 // "expr += 1", which is what a pre-increment does as well.
395 Q_FALLTHROUGH();
396 }
397 case PreIncrement: {
398 Reference e = expr.asLValue();
399 e.loadInAccumulator();
400 Instruction::Increment inc = {};
401 bytecodeGenerator->addInstruction(inc);
402 if (exprAccept(nx))
403 return e.storeConsumeAccumulator();
404 else
405 return e.storeRetainAccumulator();
406 }
407 case PostDecrement:
408 if (!exprAccept(nx) || requiresReturnValue) {
409 Reference e = expr.asLValue();
410 e.loadInAccumulator();
411 Instruction::UPlus uplus = {};
412 bytecodeGenerator->addInstruction(uplus);
413 Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator();
414 Instruction::Decrement dec = {};
415 bytecodeGenerator->addInstruction(dec);
416 e.storeConsumeAccumulator();
417 return originalValue;
418 } else {
419 // intentionally fall-through: the result is never used, so it's equivalent to
420 // "expr -= 1", which is what a pre-decrement does as well.
421 Q_FALLTHROUGH();
422 }
423 case PreDecrement: {
424 Reference e = expr.asLValue();
425 e.loadInAccumulator();
426 Instruction::Decrement dec = {};
427 bytecodeGenerator->addInstruction(dec);
428 if (exprAccept(nx))
429 return e.storeConsumeAccumulator();
430 else
431 return e.storeRetainAccumulator();
432 }
433 }
434
435 Q_UNREACHABLE();
436}
437
438void Codegen::addCJump()
439{
440 const Result &expression = currentExpr();
441 bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(),
442 expression.iftrue(), expression.iffalse());
443}
444
445void Codegen::statement(Statement *ast)
446{
447 RegisterScope scope(this);
448
449 bytecodeGenerator->incrementStatement();
450 bytecodeGenerator->setLocation(ast->firstSourceLocation());
451
452 VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
453 qSwap(_volatileMemoryLocations, vLocs);
454 accept(ast);
455 qSwap(_volatileMemoryLocations, vLocs);
456}
457
458void Codegen::statement(ExpressionNode *ast)
459{
460 if (! ast) {
461 return;
462 } else {
463 RegisterScope scope(this);
464
465 bytecodeGenerator->incrementStatement();
466 pushExpr(Result(nx));
467 VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
468 qSwap(_volatileMemoryLocations, vLocs);
469
470 accept(ast);
471
472 qSwap(_volatileMemoryLocations, vLocs);
473 Reference result = popResult();
474
475 if (hasError())
476 return;
477 if (result.loadTriggersSideEffect())
478 result.loadInAccumulator(); // triggers side effects
479 }
480}
481
482void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
483 const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition)
484{
485 if (hasError())
486 return;
487
488 if (!ast)
489 return;
490
491 pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition));
492 accept(ast);
493 Result r = popExpr();
494
495 if (hasError())
496 return;
497
498 if (r.format() == ex) {
499 Q_ASSERT(iftrue == r.iftrue());
500 Q_ASSERT(iffalse == r.iffalse());
501 Q_ASSERT(r.result().isValid());
502 bytecodeGenerator->setLocation(ast->firstSourceLocation());
503 r.result().loadInAccumulator();
504 if (r.trueBlockFollowsCondition())
505 bytecodeGenerator->jumpFalse().link(*r.iffalse());
506 else
507 bytecodeGenerator->jumpTrue().link(*r.iftrue());
508 }
509}
510
511void Codegen::program(Program *ast)
512{
513 if (ast) {
514 statementList(ast->statements);
515 }
516}
517
523
524static CompletionState completionState(StatementList *list)
525{
526 for (StatementList *it = list; it; it = it->next) {
527 if (it->statement->kind == Statement::Kind_BreakStatement ||
528 it->statement->kind == Statement::Kind_ContinueStatement)
530 if (it->statement->kind == Statement::Kind_EmptyStatement ||
531 it->statement->kind == Statement::Kind_VariableDeclaration ||
532 it->statement->kind == Statement::Kind_FunctionDeclaration)
533 continue;
534 if (it->statement->kind == Statement::Kind_Block) {
535 CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements);
536 if (subState != CompletionState::Empty)
537 return subState;
538 continue;
539 }
541 }
543}
544
545static Node *completionStatement(StatementList *list)
546{
547 Node *completionStatement = nullptr;
548 for (StatementList *it = list; it; it = it->next) {
549 if (it->statement->kind == Statement::Kind_BreakStatement ||
550 it->statement->kind == Statement::Kind_ContinueStatement)
551 return completionStatement;
552 if (it->statement->kind == Statement::Kind_ThrowStatement ||
553 it->statement->kind == Statement::Kind_ReturnStatement)
554 return it->statement;
555 if (it->statement->kind == Statement::Kind_EmptyStatement ||
556 it->statement->kind == Statement::Kind_VariableStatement ||
557 it->statement->kind == Statement::Kind_FunctionDeclaration)
558 continue;
559 if (it->statement->kind == Statement::Kind_Block) {
560 CompletionState state = completionState(static_cast<Block *>(it->statement)->statements);
561 switch (state) {
563 continue;
565 return it->statement;
567 break;
568 }
569 }
570 completionStatement = it->statement;
571 }
572 return completionStatement;
573}
574
575void Codegen::statementList(StatementList *ast)
576{
577 if (!ast)
578 return;
579
580 bool _requiresReturnValue = requiresReturnValue;
581 // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break
582 // statement will not be used, but it's at least spec compliant
583 if (!controlFlow || !controlFlow->hasLoop())
584 requiresReturnValue = false;
585
586 Node *needsCompletion = nullptr;
587
588 if (_requiresReturnValue && !requiresReturnValue)
589 needsCompletion = completionStatement(ast);
590
591 if (requiresReturnValue && !needsCompletion && !insideSwitch) {
592 // break or continue is the first real statement, set the return value to undefined
593 Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
594 }
595
596 bool _insideSwitch = insideSwitch;
597 insideSwitch = false;
598
599 for (StatementList *it = ast; it; it = it->next) {
600 if (it->statement == needsCompletion)
601 requiresReturnValue = true;
602 if (Statement *s = it->statement->statementCast())
603 statement(s);
604 else
605 statement(static_cast<ExpressionNode *>(it->statement));
606 if (it->statement == needsCompletion)
607 requiresReturnValue = false;
608 if (it->statement->kind == Statement::Kind_ThrowStatement
609 || it->statement->kind == Statement::Kind_BreakStatement
610 || it->statement->kind == Statement::Kind_ContinueStatement
611 || it->statement->kind == Statement::Kind_ReturnStatement) {
612
613 if (Visitor *visitor = _interface->unreachableVisitor())
614 Node::accept(it->next, visitor);
615 break;
616 }
617 }
618 requiresReturnValue = _requiresReturnValue;
619 insideSwitch = _insideSwitch;
620}
621
622void Codegen::variableDeclaration(PatternElement *ast)
623{
624 TailCallBlocker blockTailCalls(this);
625 RegisterScope scope(this);
626
627 if (!ast->initializer) {
628 if (ast->isLexicallyScoped()) {
629 Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
630 Reference varToStore = targetForPatternElement(ast);
631 varToStore.storeConsumeAccumulator();
632 }
633 return;
634 }
635 initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
636}
637
638void Codegen::variableDeclarationList(VariableDeclarationList *ast)
639{
640 for (VariableDeclarationList *it = ast; it; it = it->next) {
641 variableDeclaration(it->declaration);
642 }
643}
644
645Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
646{
647 if (!p->bindingIdentifier.isNull())
648 return referenceForName(p->bindingIdentifier.toString(), true, p->firstSourceLocation());
649 if (!p->bindingTarget || p->destructuringPattern())
650 return Codegen::Reference::fromStackSlot(this);
651 Reference lhs = expression(p->bindingTarget);
652 if (hasError())
653 return lhs;
654 if (!lhs.isLValue()) {
655 throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference."));
656 return lhs;
657 }
658 lhs = lhs.asLValue();
659 return lhs;
660}
661
662void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition)
663{
664 Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement);
665 RegisterScope scope(this);
666 Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base;
667 Reference varToStore = targetForPatternElement(e);
668 if (isDefinition)
669 varToStore.isReferenceToConst = false;
670 if (hasError())
671 return;
672
673 accept(e->typeAnnotation);
674
675 if (e->initializer) {
676 if (!baseRef.isValid()) {
677 // assignment
678 Reference expr = expression(e->initializer);
679 if (hasError())
680 return;
681 expr.loadInAccumulator();
682 varToStore.storeConsumeAccumulator();
683 } else if (baseRef == varToStore) {
684 baseRef.loadInAccumulator();
685 BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
686 Reference expr = expression(e->initializer);
687 if (hasError()) {
688 jump.link();
689 return;
690 }
691 expr.loadInAccumulator();
692 varToStore.storeConsumeAccumulator();
693 jump.link();
694 } else {
695 baseRef.loadInAccumulator();
696 BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
697 Reference expr = expression(e->initializer);
698 if (hasError()) {
699 jump.link();
700 return;
701 }
702 expr.loadInAccumulator();
703 jump.link();
704 varToStore.storeConsumeAccumulator();
705 }
706 } else if (baseRef != varToStore && baseRef.isValid()) {
707 baseRef.loadInAccumulator();
708 varToStore.storeConsumeAccumulator();
709 }
710 Pattern *p = e->destructuringPattern();
711 if (!p)
712 return;
713
714 if (!varToStore.isStackSlot())
715 varToStore = varToStore.storeOnStack();
716 if (PatternElementList *l = e->elementList()) {
717 destructureElementList(varToStore, l, isDefinition);
718 } else if (PatternPropertyList *p = e->propertyList()) {
719 destructurePropertyList(varToStore, p, isDefinition);
720 } else if (e->bindingTarget) {
721 // empty binding pattern. For spec compatibility, try to coerce the argument to an object
722 varToStore.loadInAccumulator();
723 Instruction::ToObject toObject;
724 bytecodeGenerator->addInstruction(toObject);
725 return;
726 }
727}
728
729Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name)
730{
731 AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name);
732 Reference property;
733 if (cname) {
734 Reference computedName = expression(cname->expression);
735 if (hasError())
736 return Reference();
737 computedName = computedName.storeOnStack();
738 property = Reference::fromSubscript(object, computedName).asLValue();
739 } else {
740 QString propertyName = name->asString();
741 property = Reference::fromMember(object, propertyName);
742 }
743 return property;
744}
745
746void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition)
747{
748 RegisterScope scope(this);
749
750 object.loadInAccumulator();
751 Instruction::ThrowOnNullOrUndefined t;
752 bytecodeGenerator->addInstruction(t);
753
754 for (PatternPropertyList *it = bindingList; it; it = it->next) {
755 PatternProperty *p = it->property;
756 RegisterScope scope(this);
757 Reference property = referenceForPropertyName(object, p->name);
758 if (hasError())
759 return;
760 initializeAndDestructureBindingElement(p, property, isDefinition);
761 if (hasError())
762 return;
763 }
764}
765
766void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition)
767{
768 RegisterScope scope(this);
769
770 Reference iterator = Reference::fromStackSlot(this);
771 QVarLengthArray<Reference> iteratorValues;
772 Reference ignored;
773
774 array.loadInAccumulator();
775 Instruction::GetIterator iteratorObjInstr;
776 iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
777 bytecodeGenerator->addInstruction(iteratorObjInstr);
778 iterator.storeConsumeAccumulator();
779
780 BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
781 Reference needsClose = Reference::storeConstOnStack(this, Encode(false));
782
783 for (PatternElementList *p = bindingList; p; p = p->next) {
784 PatternElement *e = p->element;
785 for (Elision *elision = p->elision; elision; elision = elision->next) {
786 iterator.loadInAccumulator();
787 Instruction::IteratorNext next;
788 if (!ignored.isValid())
789 ignored = Reference::fromStackSlot(this);
790 next.value = ignored.stackSlot();
791 bytecodeGenerator->addJumpInstruction(next).link(done);
792 }
793
794 if (!e)
795 continue;
796
797 if (e->type != PatternElement::RestElement) {
798 iterator.loadInAccumulator();
799 Instruction::IteratorNext next;
800 iteratorValues.push_back(Reference::fromStackSlot(this));
801 next.value = iteratorValues.back().stackSlot();
802 bytecodeGenerator->addJumpInstruction(next).link(done);
803 }
804 }
805
806 // If we've iterated through all the patterns without exhausing the iterator, it needs
807 // to be closed. But we don't close it here because:
808 // a, closing might throw an exception and we want to assign the values before we handle that
809 // b, there might be a rest element that could still continue iterating
810 Reference::fromConst(this, Encode(true)).storeOnStack(needsClose.stackSlot());
811
812 done.link();
813 bytecodeGenerator->checkException();
814
815 {
816 ControlFlowUnwindCleanup flow(this, [&]() {
817 BytecodeGenerator::Label skipClose = bytecodeGenerator->newLabel();
818 needsClose.loadInAccumulator();
819 bytecodeGenerator->jumpFalse().link(skipClose);
820 iterator.loadInAccumulator();
821 Instruction::IteratorClose close;
822 bytecodeGenerator->addInstruction(close);
823 skipClose.link();
824 });
825
826 auto it = iteratorValues.constBegin();
827 for (PatternElementList *p = bindingList; p; p = p->next) {
828 PatternElement *e = p->element;
829
830 if (!e)
831 continue;
832
833 if (e->type == PatternElement::RestElement) {
834 Q_ASSERT(it == iteratorValues.constEnd());
835
836 // The rest element is guaranteed to exhaust the iterator
837 Reference::fromConst(this, Encode(false)).storeOnStack(needsClose.stackSlot());
838
839 iterator.loadInAccumulator();
840 bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
841 initializeAndDestructureBindingElement(
842 e, Reference::fromAccumulator(this), isDefinition);
843 } else {
844 Q_ASSERT(it != iteratorValues.constEnd());
845 initializeAndDestructureBindingElement(e, *it++, isDefinition);
846 }
847
848 if (hasError())
849 return;
850 }
851 }
852}
853
854void Codegen::destructurePattern(Pattern *p, const Reference &rhs)
855{
856 RegisterScope scope(this);
857 if (auto *o = AST::cast<ObjectPattern *>(p))
858 destructurePropertyList(rhs, o->properties);
859 else if (auto *a = AST::cast<ArrayPattern *>(p))
860 destructureElementList(rhs, a->elements);
861 else
862 Q_UNREACHABLE();
863}
864
865
866bool Codegen::visit(ArgumentList *)
867{
868 Q_UNREACHABLE_RETURN(false);
869}
870
871bool Codegen::visit(CaseBlock *)
872{
873 Q_UNREACHABLE_RETURN(false);
874}
875
876bool Codegen::visit(CaseClause *)
877{
878 Q_UNREACHABLE_RETURN(false);
879}
880
881bool Codegen::visit(CaseClauses *)
882{
883 Q_UNREACHABLE_RETURN(false);
884}
885
886bool Codegen::visit(Catch *)
887{
888 Q_UNREACHABLE_RETURN(false);
889}
890
891bool Codegen::visit(DefaultClause *)
892{
893 Q_UNREACHABLE_RETURN(false);
894}
895
896bool Codegen::visit(Elision *)
897{
898 Q_UNREACHABLE_RETURN(false);
899}
900
901bool Codegen::visit(Finally *)
902{
903 Q_UNREACHABLE_RETURN(false);
904}
905
906bool Codegen::visit(FormalParameterList *)
907{
908 Q_UNREACHABLE_RETURN(false);
909}
910
911bool Codegen::visit(Program *)
912{
913 Q_UNREACHABLE_RETURN(false);
914}
915
916bool Codegen::visit(PatternElement *)
917{
918 Q_UNREACHABLE_RETURN(false);
919}
920
921bool Codegen::visit(PatternElementList *)
922{
923 Q_UNREACHABLE_RETURN(false);
924}
925
926bool Codegen::visit(PatternProperty *)
927{
928 Q_UNREACHABLE_RETURN(false);
929}
930
931bool Codegen::visit(PatternPropertyList *)
932{
933 Q_UNREACHABLE_RETURN(false);
934}
935
936bool Codegen::visit(ExportDeclaration *ast)
937{
938 if (!ast->exportDefault)
939 return true;
940
941 TailCallBlocker blockTailCalls(this);
942 Reference exportedValue;
943
944 if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
945 pushExpr();
946 visit(static_cast<FunctionExpression*>(fdecl));
947 exportedValue = popResult();
948 } else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
949 pushExpr();
950 visit(static_cast<ClassExpression*>(classDecl));
951 exportedValue = popResult();
952 } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
953 exportedValue = expression(expr);
954 }
955
956 exportedValue.loadInAccumulator();
957
958 const int defaultExportIndex = _context->locals.indexOf(_context->localNameForDefaultExport);
959 Q_ASSERT(defaultExportIndex != -1);
960 Reference defaultExportSlot = Reference::fromScopedLocal(this, defaultExportIndex, /*scope*/0);
961 defaultExportSlot.storeConsumeAccumulator();
962
963 return false;
964}
965
966bool Codegen::visit(TypeAnnotation *ast)
967{
968 throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Type annotations are not supported (yet)."));
969 return false;
970}
971
972bool Codegen::visit(StatementList *)
973{
974 Q_UNREACHABLE_RETURN(false);
975}
976
977bool Codegen::visit(UiArrayMemberList *)
978{
979 Q_UNREACHABLE_RETURN(false);
980}
981
982bool Codegen::visit(UiImport *)
983{
984 Q_UNREACHABLE_RETURN(false);
985}
986
987bool Codegen::visit(UiHeaderItemList *)
988{
989 Q_UNREACHABLE_RETURN(false);
990}
991
992bool Codegen::visit(UiPragmaValueList *)
993{
994 Q_UNREACHABLE_RETURN(false);
995}
996
997bool Codegen::visit(UiPragma *)
998{
999 Q_UNREACHABLE_RETURN(false);
1000}
1001
1002bool Codegen::visit(UiObjectInitializer *)
1003{
1004 Q_UNREACHABLE_RETURN(false);
1005}
1006
1007bool Codegen::visit(UiObjectMemberList *)
1008{
1009 Q_UNREACHABLE_RETURN(false);
1010}
1011
1012bool Codegen::visit(UiParameterList *)
1013{
1014 Q_UNREACHABLE_RETURN(false);
1015}
1016
1017bool Codegen::visit(UiProgram *)
1018{
1019 Q_UNREACHABLE_RETURN(false);
1020}
1021
1022bool Codegen::visit(UiQualifiedId *)
1023{
1024 Q_UNREACHABLE_RETURN(false);
1025}
1026
1027bool Codegen::visit(VariableDeclarationList *)
1028{
1029 Q_UNREACHABLE_RETURN(false);
1030}
1031
1032bool Codegen::visit(ClassExpression *ast)
1033{
1034 TailCallBlocker blockTailCalls(this);
1035
1036 Compiler::Class jsClass;
1037 jsClass.nameIndex = registerString(ast->name.toString());
1038
1039 ClassElementList *constructor = nullptr;
1040 int nComputedNames = 0;
1041 int nStaticComputedNames = 0;
1042
1043 RegisterScope scope(this);
1044 ControlFlowBlock controlFlow(this, ast);
1045
1046 for (auto *member = ast->elements; member; member = member->next) {
1047 PatternProperty *p = member->property;
1048 FunctionExpression *f = p->initializer->asFunctionDefinition();
1049 Q_ASSERT(f);
1050 AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name);
1051 if (cname) {
1052 ++nComputedNames;
1053 if (member->isStatic)
1054 ++nStaticComputedNames;
1055 }
1056 QString name = p->name->asString();
1057 uint nameIndex = cname ? UINT_MAX : registerString(name);
1058 Compiler::Class::Method::Type type = Compiler::Class::Method::Regular;
1059 if (p->type == PatternProperty::Getter)
1060 type = Compiler::Class::Method::Getter;
1061 else if (p->type == PatternProperty::Setter)
1062 type = Compiler::Class::Method::Setter;
1063 Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) };
1064
1065 if (member->isStatic) {
1066 if (name == QStringLiteral("prototype")) {
1067 throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'."));
1068 return false;
1069 }
1070 jsClass.staticMethods << m;
1071 } else {
1072 if (name == QStringLiteral("constructor")) {
1073 if (constructor) {
1074 throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class."));
1075 return false;
1076 }
1077 if (m.type != Compiler::Class::Method::Regular) {
1078 throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'."));
1079 return false;
1080 }
1081 constructor = member;
1082 jsClass.constructorIndex = m.functionIndex;
1083 continue;
1084 }
1085
1086 jsClass.methods << m;
1087 }
1088 }
1089
1090 int classIndex = _module->classes.size();
1091 _module->classes.append(jsClass);
1092
1093 Reference heritage = Reference::fromStackSlot(this);
1094 if (ast->heritage) {
1095 bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
1096 Reference r = expression(ast->heritage);
1097 if (hasError())
1098 return false;
1099 r.storeOnStack(heritage.stackSlot());
1100 } else {
1101 Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator();
1102 heritage.storeConsumeAccumulator();
1103 }
1104
1105 int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0;
1106 int currentStaticName = computedNames;
1107 int currentNonStaticName = computedNames + nStaticComputedNames;
1108
1109 for (auto *member = ast->elements; member; member = member->next) {
1110 AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name);
1111 if (!cname)
1112 continue;
1113 RegisterScope scope(this);
1114 bytecodeGenerator->setLocation(cname->firstSourceLocation());
1115 Reference computedName = expression(cname->expression);
1116 if (hasError())
1117 return false;
1118 computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
1119 }
1120
1121 Instruction::CreateClass createClass;
1122 createClass.classIndex = classIndex;
1123 createClass.heritage = heritage.stackSlot();
1124 createClass.computedNames = computedNames;
1125
1126 bytecodeGenerator->addInstruction(createClass);
1127
1128 if (!ast->name.isEmpty()) {
1129 Reference ctor = referenceForName(ast->name.toString(), true);
1130 ctor.isReferenceToConst = false; // this is the definition
1131 (void) ctor.storeRetainAccumulator();
1132 }
1133
1134 setExprResult(Reference::fromAccumulator(this));
1135 return false;
1136}
1137
1138bool Codegen::visit(ClassDeclaration *ast)
1139{
1140 TailCallBlocker blockTailCalls(this);
1141 Reference outerVar = referenceForName(ast->name.toString(), true);
1142 visit(static_cast<ClassExpression *>(ast));
1143 (void) outerVar.storeRetainAccumulator();
1144 return false;
1145}
1146
1147bool Codegen::visit(CommaExpression *ast)
1148{
1149 if (hasError())
1150 return false;
1151
1152 TailCallBlocker blockTailCalls(this);
1153 statement(ast->left);
1154 blockTailCalls.unblock();
1155 clearExprResultName(); // The name only holds for the left part
1156 accept(ast->right);
1157 return false;
1158}
1159
1160bool Codegen::visit(ArrayPattern *ast)
1161{
1162 if (hasError())
1163 return false;
1164
1165 TailCallBlocker blockTailCalls(this);
1166
1167 PatternElementList *it = ast->elements;
1168
1169 int argc = 0;
1170 {
1171 RegisterScope scope(this);
1172
1173 int args = -1;
1174 auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
1175 int temp = bytecodeGenerator->newRegister();
1176 if (args == -1)
1177 args = temp;
1178 if (!arg) {
1179 auto c = Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue());
1180 (void) c.storeOnStack(temp);
1181 } else {
1182 RegisterScope scope(this);
1183 Reference r = expression(arg);
1184 if (hasError())
1185 return;
1186 (void) r.storeOnStack(temp);
1187 }
1188 ++argc;
1189 };
1190
1191 for (; it; it = it->next) {
1192 PatternElement *e = it->element;
1193 if (e && e->type == PatternElement::SpreadElement)
1194 break;
1195 for (Elision *elision = it->elision; elision; elision = elision->next)
1196 push(nullptr);
1197
1198 if (!e)
1199 continue;
1200
1201 push(e->initializer);
1202 if (hasError())
1203 return false;
1204 }
1205
1206 if (args == -1) {
1207 Q_ASSERT(argc == 0);
1208 args = 0;
1209 }
1210
1211 Instruction::DefineArray call;
1212 call.argc = argc;
1213 call.args = Moth::StackSlot::createRegister(args);
1214 bytecodeGenerator->addInstruction(call);
1215 }
1216
1217 if (!it) {
1218 setExprResult(Reference::fromAccumulator(this));
1219 return false;
1220 }
1221 Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
1222
1223 RegisterScope scope(this);
1224 Reference array = Reference::fromStackSlot(this);
1225 array.storeConsumeAccumulator();
1226 Reference index = Reference::storeConstOnStack(this, Encode(argc));
1227
1228 auto pushAccumulator = [&]() {
1229 Reference slot = Reference::fromSubscript(array, index);
1230 slot.storeConsumeAccumulator();
1231
1232 index.loadInAccumulator();
1233 Instruction::Increment inc = {};
1234 bytecodeGenerator->addInstruction(inc);
1235 index.storeConsumeAccumulator();
1236 };
1237
1238 while (it) {
1239 for (Elision *elision = it->elision; elision; elision = elision->next) {
1240 Reference::fromConst(
1241 this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator();
1242 pushAccumulator();
1243 }
1244
1245 if (!it->element) {
1246 it = it->next;
1247 continue;
1248 }
1249
1250 // handle spread element
1251 if (it->element->type == PatternElement::SpreadElement) {
1252 RegisterScope scope(this);
1253
1254 Reference iterator = Reference::fromStackSlot(this);
1255 Reference lhsValue = Reference::fromStackSlot(this);
1256
1257 // There should be a temporal block, so that variables declared in lhs shadow outside vars.
1258 // This block should define a temporal dead zone for those variables, which is not yet implemented.
1259 {
1260 RegisterScope innerScope(this);
1261 Reference expr = expression(it->element->initializer);
1262 if (hasError())
1263 return false;
1264
1265 expr.loadInAccumulator();
1266 Instruction::GetIterator iteratorObjInstr;
1267 iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
1268 bytecodeGenerator->addInstruction(iteratorObjInstr);
1269 iterator.storeConsumeAccumulator();
1270 }
1271
1272 BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
1273 BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
1274 BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
1275
1276 {
1277 auto cleanup = [this, iterator, done]() {
1278 iterator.loadInAccumulator();
1279 Instruction::IteratorClose close;
1280 bytecodeGenerator->addInstruction(close);
1281 done.link();
1282 };
1283 ControlFlowLoop flow(this, &end, &in, std::move(cleanup));
1284
1285 in.link();
1286 bytecodeGenerator->addLoopStart(in);
1287 iterator.loadInAccumulator();
1288 Instruction::IteratorNext next;
1289 next.value = lhsValue.stackSlot();
1290 bytecodeGenerator->addJumpInstruction(next).link(done);
1291
1292 lhsValue.loadInAccumulator();
1293 pushAccumulator();
1294
1295 bytecodeGenerator->checkException();
1296 bytecodeGenerator->jump().link(in);
1297 end.link();
1298 }
1299 } else {
1300 RegisterScope innerScope(this);
1301 Reference expr = expression(it->element->initializer);
1302 if (hasError())
1303 return false;
1304
1305 expr.loadInAccumulator();
1306 pushAccumulator();
1307 }
1308
1309 it = it->next;
1310 }
1311
1312 array.loadInAccumulator();
1313 setExprResult(Reference::fromAccumulator(this));
1314
1315 return false;
1316}
1317
1318bool Codegen::visit(ArrayMemberExpression *ast)
1319{
1320 if (hasError())
1321 return false;
1322
1323 const bool isTailOfChain = traverseOptionalChain(ast);
1324
1325 TailCallBlocker blockTailCalls(this);
1326 Reference base = expression(ast->base);
1327
1328 auto writeSkip = [&]() {
1329 base.loadInAccumulator();
1330 bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
1331 auto jumpToUndefined = bytecodeGenerator->jumpTrue();
1332 m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
1333 };
1334
1335 if (hasError())
1336 return false;
1337 if (base.isSuper()) {
1338 Reference index = expression(ast->expression).storeOnStack();
1339 optionalChainFinalizer(Reference::fromSuperProperty(index), isTailOfChain);
1340 return false;
1341 }
1342 base = base.storeOnStack();
1343 if (hasError())
1344 return false;
1345 if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
1346 QString s = str->value.toString();
1347 uint arrayIndex = stringToArrayIndex(s);
1348 if (arrayIndex == UINT_MAX) {
1349 auto ref = Reference::fromMember(base, s, ast->expression->firstSourceLocation(),
1350 ast->isOptional,
1351 &m_optionalChainsStates.top().jumpsToPatch);
1352 setExprResult(ref);
1353 optionalChainFinalizer(ref, isTailOfChain);
1354 return false;
1355 }
1356
1357 if (ast->isOptional)
1358 writeSkip();
1359
1360 Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
1361 optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain);
1362 return false;
1363 }
1364
1365
1366 if (ast->isOptional)
1367 writeSkip();
1368
1369 Reference index = expression(ast->expression);
1370
1371 if (hasError())
1372 return false;
1373
1374 optionalChainFinalizer(Reference::fromSubscript(base, index), isTailOfChain);
1375 return false;
1376}
1377
1378static QSOperator::Op baseOp(int op)
1379{
1380 switch ((QSOperator::Op) op) {
1381 case QSOperator::InplaceAnd: return QSOperator::BitAnd;
1382 case QSOperator::InplaceSub: return QSOperator::Sub;
1383 case QSOperator::InplaceDiv: return QSOperator::Div;
1384 case QSOperator::InplaceAdd: return QSOperator::Add;
1385 case QSOperator::InplaceLeftShift: return QSOperator::LShift;
1386 case QSOperator::InplaceMod: return QSOperator::Mod;
1387 case QSOperator::InplaceExp: return QSOperator::Exp;
1388 case QSOperator::InplaceMul: return QSOperator::Mul;
1389 case QSOperator::InplaceOr: return QSOperator::BitOr;
1390 case QSOperator::InplaceRightShift: return QSOperator::RShift;
1391 case QSOperator::InplaceURightShift: return QSOperator::URShift;
1392 case QSOperator::InplaceXor: return QSOperator::BitXor;
1393 default: return QSOperator::Invalid;
1394 }
1395}
1396
1397bool Codegen::visit(BinaryExpression *ast)
1398{
1399 if (hasError())
1400 return false;
1401
1402 TailCallBlocker blockTailCalls(this);
1403
1404 if (ast->op == QSOperator::And) {
1405 if (exprAccept(cx)) {
1406 auto iftrue = bytecodeGenerator->newLabel();
1407 condition(ast->left, &iftrue, currentExpr().iffalse(), true);
1408 iftrue.link();
1409 blockTailCalls.unblock();
1410 const Result &expr = currentExpr();
1411 condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
1412 } else {
1413 auto iftrue = bytecodeGenerator->newLabel();
1414 auto endif = bytecodeGenerator->newLabel();
1415
1416 Reference left = expression(ast->left);
1417 if (hasError())
1418 return false;
1419 left.loadInAccumulator();
1420
1421 bytecodeGenerator->setLocation(ast->operatorToken);
1422 bytecodeGenerator->jumpFalse().link(endif);
1423 iftrue.link();
1424
1425 blockTailCalls.unblock();
1426 Reference right = expression(ast->right);
1427 if (hasError())
1428 return false;
1429 right.loadInAccumulator();
1430
1431 endif.link();
1432
1433 setExprResult(Reference::fromAccumulator(this));
1434 }
1435 return false;
1436 } else if (ast->op == QSOperator::Or) {
1437 if (exprAccept(cx)) {
1438 auto iffalse = bytecodeGenerator->newLabel();
1439 condition(ast->left, currentExpr().iftrue(), &iffalse, false);
1440 iffalse.link();
1441 const Result &expr = currentExpr();
1442 condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
1443 } else {
1444 auto iffalse = bytecodeGenerator->newLabel();
1445 auto endif = bytecodeGenerator->newLabel();
1446
1447 Reference left = expression(ast->left);
1448 if (hasError())
1449 return false;
1450 left.loadInAccumulator();
1451
1452 bytecodeGenerator->setLocation(ast->operatorToken);
1453 bytecodeGenerator->jumpTrue().link(endif);
1454 iffalse.link();
1455
1456 blockTailCalls.unblock();
1457 Reference right = expression(ast->right);
1458 if (hasError())
1459 return false;
1460 right.loadInAccumulator();
1461
1462 endif.link();
1463
1464 setExprResult(Reference::fromAccumulator(this));
1465 }
1466 return false;
1467 } else if (ast->op == QSOperator::Coalesce) {
1468
1469 Reference left = expression(ast->left);
1470 if (hasError())
1471 return false;
1472
1473 BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
1474
1475 Instruction::CmpEqNull cmp;
1476
1477 left = left.storeOnStack();
1478 left.loadInAccumulator();
1479 bytecodeGenerator->addInstruction(cmp);
1480
1481 bytecodeGenerator->jumpTrue().link(iftrue);
1482
1483 blockTailCalls.unblock();
1484
1485 left.loadInAccumulator();
1486 BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
1487 iftrue.link();
1488
1489 Reference right = expression(ast->right);
1490 right.loadInAccumulator();
1491 jump_endif.link();
1492 setExprResult(Reference::fromAccumulator(this));
1493
1494 return false;
1495 } else if (ast->op == QSOperator::Assign) {
1496 bytecodeGenerator->setLocation(ast->left->firstSourceLocation());
1497 if (AST::Pattern *p = ast->left->patternCast()) {
1498 RegisterScope scope(this);
1499 Reference right = expression(ast->right);
1500 if (hasError())
1501 return false;
1502 right = right.storeOnStack();
1503 destructurePattern(p, right);
1504 if (!exprAccept(nx)) {
1505 right.loadInAccumulator();
1506 setExprResult(Reference::fromAccumulator(this));
1507 }
1508 return false;
1509 }
1510 Reference left = expression(ast->left);
1511 if (hasError())
1512 return false;
1513
1514 if (!left.isLValue()) {
1515 throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
1516 return false;
1517 }
1518 left = left.asLValue();
1519 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
1520 return false;
1521 blockTailCalls.unblock();
1522 Reference r = expression(ast->right);
1523 if (hasError())
1524 return false;
1525 r.loadInAccumulator();
1526 if (exprAccept(nx))
1527 setExprResult(left.storeConsumeAccumulator());
1528 else
1529 setExprResult(left.storeRetainAccumulator());
1530 return false;
1531 }
1532
1533 Reference left = expression(ast->left);
1534 if (hasError())
1535 return false;
1536
1537 switch (ast->op) {
1538 case QSOperator::Or:
1539 case QSOperator::And:
1540 case QSOperator::Assign:
1541 Q_UNREACHABLE(); // handled separately above
1542 break;
1543
1544 case QSOperator::InplaceAnd:
1545 case QSOperator::InplaceSub:
1546 case QSOperator::InplaceDiv:
1547 case QSOperator::InplaceAdd:
1548 case QSOperator::InplaceLeftShift:
1549 case QSOperator::InplaceMod:
1550 case QSOperator::InplaceExp:
1551 case QSOperator::InplaceMul:
1552 case QSOperator::InplaceOr:
1553 case QSOperator::InplaceRightShift:
1554 case QSOperator::InplaceURightShift:
1555 case QSOperator::InplaceXor: {
1556 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
1557 return false;
1558
1559 if (!left.isLValue()) {
1560 throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue"));
1561 return false;
1562 }
1563 left = left.asLValue();
1564
1565 Reference tempLeft = left.storeOnStack();
1566 Reference right = expression(ast->right);
1567
1568 if (hasError())
1569 return false;
1570
1571 binopHelper(ast, baseOp(ast->op), tempLeft, right).loadInAccumulator();
1572 setExprResult(left.storeRetainAccumulator());
1573
1574 break;
1575 }
1576
1577 case QSOperator::BitAnd:
1578 case QSOperator::BitOr:
1579 case QSOperator::BitXor:
1580 if (left.isConstant()) {
1581 Reference right = expression(ast->right);
1582 if (hasError())
1583 return false;
1584 setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), right, left));
1585 break;
1586 }
1587 Q_FALLTHROUGH();
1588 case QSOperator::In:
1589 case QSOperator::InstanceOf:
1590 case QSOperator::As:
1591 case QSOperator::Equal:
1592 case QSOperator::NotEqual:
1593 case QSOperator::Ge:
1594 case QSOperator::Gt:
1595 case QSOperator::Le:
1596 case QSOperator::Lt:
1597 case QSOperator::StrictEqual:
1598 case QSOperator::StrictNotEqual:
1599 case QSOperator::Add:
1600 case QSOperator::Div:
1601 case QSOperator::Exp:
1602 case QSOperator::Mod:
1603 case QSOperator::Mul:
1604 case QSOperator::Sub:
1605 case QSOperator::LShift:
1606 case QSOperator::RShift:
1607 case QSOperator::URShift: {
1608 Reference right;
1609 if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
1610 visit(rhs);
1611 right = exprResult();
1612 } else {
1613 left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
1614 right = expression(ast->right);
1615 }
1616 if (hasError())
1617 return false;
1618
1619 setExprResult(binopHelper(ast, static_cast<QSOperator::Op>(ast->op), left, right));
1620
1621 break;
1622 }
1623 } // switch
1624
1625 return false;
1626}
1627
1628Codegen::Reference Codegen::binopHelper(BinaryExpression *ast, QSOperator::Op oper, Reference &left,
1629 Reference &right)
1630{
1631 auto loc = combine(ast->left->firstSourceLocation(), ast->right->lastSourceLocation());
1632 bytecodeGenerator->setLocation(loc);
1633 switch (oper) {
1634 case QSOperator::Add: {
1635 left = left.storeOnStack();
1636 right.loadInAccumulator();
1637 Instruction::Add add;
1638 add.lhs = left.stackSlot();
1639 bytecodeGenerator->addInstruction(add);
1640 break;
1641 }
1642 case QSOperator::Sub: {
1643 if (right.isConstant() && right.constant == Encode(int(1))) {
1644 left.loadInAccumulator();
1645 Instruction::Decrement dec = {};
1646 bytecodeGenerator->addInstruction(dec);
1647 } else {
1648 left = left.storeOnStack();
1649 right.loadInAccumulator();
1650 Instruction::Sub sub;
1651 sub.lhs = left.stackSlot();
1652 bytecodeGenerator->addInstruction(sub);
1653 }
1654 break;
1655 }
1656 case QSOperator::Exp: {
1657 left = left.storeOnStack();
1658 right.loadInAccumulator();
1659 Instruction::Exp exp;
1660 exp.lhs = left.stackSlot();
1661 bytecodeGenerator->addInstruction(exp);
1662 break;
1663 }
1664 case QSOperator::Mul: {
1665 left = left.storeOnStack();
1666 right.loadInAccumulator();
1667 Instruction::Mul mul;
1668 mul.lhs = left.stackSlot();
1669 bytecodeGenerator->addInstruction(mul);
1670 break;
1671 }
1672 case QSOperator::Div: {
1673 left = left.storeOnStack();
1674 right.loadInAccumulator();
1675 Instruction::Div div;
1676 div.lhs = left.stackSlot();
1677 bytecodeGenerator->addInstruction(div);
1678 break;
1679 }
1680 case QSOperator::Mod: {
1681 left = left.storeOnStack();
1682 right.loadInAccumulator();
1683 Instruction::Mod mod;
1684 mod.lhs = left.stackSlot();
1685 bytecodeGenerator->addInstruction(mod);
1686 break;
1687 }
1688 case QSOperator::BitAnd:
1689 if (right.isConstant()) {
1690 int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
1691 if (left.isConstant()) {
1692 int result = StaticValue::fromReturnedValue(left.constant).toInt32() & rightAsInt;
1693 return Reference::fromConst(this, Encode(result));
1694 }
1695 left.loadInAccumulator();
1696 Instruction::BitAndConst bitAnd;
1697 bitAnd.rhs = rightAsInt;
1698 bytecodeGenerator->addInstruction(bitAnd);
1699 } else {
1700 right.loadInAccumulator();
1701 Instruction::BitAnd bitAnd;
1702 bitAnd.lhs = left.stackSlot();
1703 bytecodeGenerator->addInstruction(bitAnd);
1704 }
1705 break;
1706 case QSOperator::BitOr:
1707 if (right.isConstant()) {
1708 int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
1709 if (left.isConstant()) {
1710 int result = StaticValue::fromReturnedValue(left.constant).toInt32() | rightAsInt;
1711 return Reference::fromConst(this, Encode(result));
1712 }
1713 left.loadInAccumulator();
1714 Instruction::BitOrConst bitOr;
1715 bitOr.rhs = rightAsInt;
1716 bytecodeGenerator->addInstruction(bitOr);
1717 } else {
1718 right.loadInAccumulator();
1719 Instruction::BitOr bitOr;
1720 bitOr.lhs = left.stackSlot();
1721 bytecodeGenerator->addInstruction(bitOr);
1722 }
1723 break;
1724 case QSOperator::BitXor:
1725 if (right.isConstant()) {
1726 int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32();
1727 if (left.isConstant()) {
1728 int result = StaticValue::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
1729 return Reference::fromConst(this, Encode(result));
1730 }
1731 left.loadInAccumulator();
1732 Instruction::BitXorConst bitXor;
1733 bitXor.rhs = rightAsInt;
1734 bytecodeGenerator->addInstruction(bitXor);
1735 } else {
1736 right.loadInAccumulator();
1737 Instruction::BitXor bitXor;
1738 bitXor.lhs = left.stackSlot();
1739 bytecodeGenerator->addInstruction(bitXor);
1740 }
1741 break;
1742 case QSOperator::URShift:
1743 if (right.isConstant()) {
1744 left.loadInAccumulator();
1745 Instruction::UShrConst ushr;
1746 ushr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
1747 bytecodeGenerator->addInstruction(ushr);
1748 } else {
1749 right.loadInAccumulator();
1750 Instruction::UShr ushr;
1751 ushr.lhs = left.stackSlot();
1752 bytecodeGenerator->addInstruction(ushr);
1753 }
1754 break;
1755 case QSOperator::RShift:
1756 if (right.isConstant()) {
1757 left.loadInAccumulator();
1758 Instruction::ShrConst shr;
1759 shr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
1760 bytecodeGenerator->addInstruction(shr);
1761 } else {
1762 right.loadInAccumulator();
1763 Instruction::Shr shr;
1764 shr.lhs = left.stackSlot();
1765 bytecodeGenerator->addInstruction(shr);
1766 }
1767 break;
1768 case QSOperator::LShift:
1769 if (right.isConstant()) {
1770 left.loadInAccumulator();
1771 Instruction::ShlConst shl;
1772 shl.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f;
1773 bytecodeGenerator->addInstruction(shl);
1774 } else {
1775 right.loadInAccumulator();
1776 Instruction::Shl shl;
1777 shl.lhs = left.stackSlot();
1778 bytecodeGenerator->addInstruction(shl);
1779 }
1780 break;
1781 case QSOperator::InstanceOf: {
1782 Instruction::CmpInstanceOf binop;
1783 left = left.storeOnStack();
1784 right.loadInAccumulator();
1785 binop.lhs = left.stackSlot();
1786 bytecodeGenerator->addInstruction(binop);
1787 break;
1788 }
1789 case QSOperator::As: {
1790 Instruction::As as;
1791 left = left.storeOnStack();
1792 right.loadInAccumulator();
1793 as.lhs = left.stackSlot();
1794 bytecodeGenerator->addInstruction(as);
1795 break;
1796 }
1797 case QSOperator::In: {
1798 Instruction::CmpIn binop;
1799 left = left.storeOnStack();
1800 right.loadInAccumulator();
1801 binop.lhs = left.stackSlot();
1802 bytecodeGenerator->addInstruction(binop);
1803 break;
1804 }
1805 case QSOperator::StrictEqual: {
1806 if (exprAccept(cx))
1807 return jumpBinop(oper, left, right);
1808
1809 Instruction::CmpStrictEqual cmp;
1810 left = left.storeOnStack();
1811 right.loadInAccumulator();
1812 cmp.lhs = left.stackSlot();
1813 bytecodeGenerator->addInstruction(cmp);
1814 break;
1815 }
1816 case QSOperator::StrictNotEqual: {
1817 if (exprAccept(cx))
1818 return jumpBinop(oper, left, right);
1819
1820 Instruction::CmpStrictNotEqual cmp;
1821 left = left.storeOnStack();
1822 right.loadInAccumulator();
1823 cmp.lhs = left.stackSlot();
1824 bytecodeGenerator->addInstruction(cmp);
1825 break;
1826 }
1827 case QSOperator::Equal: {
1828 if (exprAccept(cx))
1829 return jumpBinop(oper, left, right);
1830
1831 Instruction::CmpEq cmp;
1832 left = left.storeOnStack();
1833 right.loadInAccumulator();
1834 cmp.lhs = left.stackSlot();
1835 bytecodeGenerator->addInstruction(cmp);
1836 break;
1837 }
1838 case QSOperator::NotEqual: {
1839 if (exprAccept(cx))
1840 return jumpBinop(oper, left, right);
1841
1842 Instruction::CmpNe cmp;
1843 left = left.storeOnStack();
1844 right.loadInAccumulator();
1845 cmp.lhs = left.stackSlot();
1846 bytecodeGenerator->addInstruction(cmp);
1847 break;
1848 }
1849 case QSOperator::Gt: {
1850 if (exprAccept(cx))
1851 return jumpBinop(oper, left, right);
1852
1853 Instruction::CmpGt cmp;
1854 left = left.storeOnStack();
1855 right.loadInAccumulator();
1856 cmp.lhs = left.stackSlot();
1857 bytecodeGenerator->addInstruction(cmp);
1858 break;
1859 }
1860 case QSOperator::Ge: {
1861 if (exprAccept(cx))
1862 return jumpBinop(oper, left, right);
1863
1864 Instruction::CmpGe cmp;
1865 left = left.storeOnStack();
1866 right.loadInAccumulator();
1867 cmp.lhs = left.stackSlot();
1868 bytecodeGenerator->addInstruction(cmp);
1869 break;
1870 }
1871 case QSOperator::Lt: {
1872 if (exprAccept(cx))
1873 return jumpBinop(oper, left, right);
1874
1875 Instruction::CmpLt cmp;
1876 left = left.storeOnStack();
1877 right.loadInAccumulator();
1878 cmp.lhs = left.stackSlot();
1879 bytecodeGenerator->addInstruction(cmp);
1880 break;
1881 }
1882 case QSOperator::Le:
1883 if (exprAccept(cx))
1884 return jumpBinop(oper, left, right);
1885
1886 Instruction::CmpLe cmp;
1887 left = left.storeOnStack();
1888 right.loadInAccumulator();
1889 cmp.lhs = left.stackSlot();
1890 bytecodeGenerator->addInstruction(cmp);
1891 break;
1892 default:
1893 Q_UNREACHABLE();
1894 }
1895
1896 return Reference::fromAccumulator(this);
1897}
1898
1899Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
1900{
1901 // See if we can generate specialized comparison instructions:
1902 if (oper == QSOperator::Equal || oper == QSOperator::NotEqual) {
1903 // Because == and != are reflexive, we can do the following:
1904 if (left.isConstant() && !right.isConstant())
1905 qSwap(left, right); // null==a -> a==null
1906
1907 if (right.isConstant()) {
1908 StaticValue c = StaticValue::fromReturnedValue(right.constant);
1909 if (c.isNull() || c.isUndefined()) {
1910 left.loadInAccumulator();
1911 if (oper == QSOperator::Equal) {
1912 Instruction::CmpEqNull cmp;
1913 bytecodeGenerator->addInstruction(cmp);
1914 addCJump();
1915 return Reference();
1916 } else if (oper == QSOperator::NotEqual) {
1917 Instruction::CmpNeNull cmp;
1918 bytecodeGenerator->addInstruction(cmp);
1919 addCJump();
1920 return Reference();
1921 }
1922 } else if (c.isInt32()) {
1923 left.loadInAccumulator();
1924 if (oper == QSOperator::Equal) {
1925 Instruction::CmpEqInt cmp;
1926 cmp.lhs = c.int_32();
1927 bytecodeGenerator->addInstruction(cmp);
1928 addCJump();
1929 return Reference();
1930 } else if (oper == QSOperator::NotEqual) {
1931 Instruction::CmpNeInt cmp;
1932 cmp.lhs = c.int_32();
1933 bytecodeGenerator->addInstruction(cmp);
1934 addCJump();
1935 return Reference();
1936 }
1937
1938 }
1939 }
1940 }
1941
1942 left = left.storeOnStack();
1943 right.loadInAccumulator();
1944
1945 switch (oper) {
1946 case QSOperator::StrictEqual: {
1947 Instruction::CmpStrictEqual cmp;
1948 cmp.lhs = left.stackSlot();
1949 bytecodeGenerator->addInstruction(cmp);
1950 addCJump();
1951 break;
1952 }
1953 case QSOperator::StrictNotEqual: {
1954 Instruction::CmpStrictNotEqual cmp;
1955 cmp.lhs = left.stackSlot();
1956 bytecodeGenerator->addInstruction(cmp);
1957 addCJump();
1958 break;
1959 }
1960 case QSOperator::Equal: {
1961 Instruction::CmpEq cmp;
1962 cmp.lhs = left.stackSlot();
1963 bytecodeGenerator->addInstruction(cmp);
1964 addCJump();
1965 break;
1966 }
1967 case QSOperator::NotEqual: {
1968 Instruction::CmpNe cmp;
1969 cmp.lhs = left.stackSlot();
1970 bytecodeGenerator->addInstruction(cmp);
1971 addCJump();
1972 break;
1973 }
1974 case QSOperator::Gt: {
1975 Instruction::CmpGt cmp;
1976 cmp.lhs = left.stackSlot();
1977 bytecodeGenerator->addInstruction(cmp);
1978 addCJump();
1979 break;
1980 }
1981 case QSOperator::Ge: {
1982 Instruction::CmpGe cmp;
1983 cmp.lhs = left.stackSlot();
1984 bytecodeGenerator->addInstruction(cmp);
1985 addCJump();
1986 break;
1987 }
1988 case QSOperator::Lt: {
1989 Instruction::CmpLt cmp;
1990 cmp.lhs = left.stackSlot();
1991 bytecodeGenerator->addInstruction(cmp);
1992 addCJump();
1993 break;
1994 }
1995 case QSOperator::Le: {
1996 Instruction::CmpLe cmp;
1997 cmp.lhs = left.stackSlot();
1998 bytecodeGenerator->addInstruction(cmp);
1999 addCJump();
2000 break;
2001 }
2002 default:
2003 Q_UNREACHABLE();
2004 }
2005 return Reference();
2006}
2007
2008Codegen::Reference Codegen::loadSubscriptForCall(const Codegen::Reference &base)
2009{
2010 // Retrieve the function to be called before generating the arguments.
2011 // Generating the arguments might change the array.
2012 base.elementSubscript.loadInAccumulator();
2013 Codegen::Instruction::LoadElement load;
2014 load.base = base.elementBase;
2015 bytecodeGenerator->addInstruction(load);
2016 return Reference::fromAccumulator(this);
2017}
2018
2019bool Codegen::visit(CallExpression *ast)
2020{
2021 if (hasError())
2022 return false;
2023
2024 const bool isTailOfChain = traverseOptionalChain(ast);
2025
2026 RegisterScope scope(this);
2027 TailCallBlocker blockTailCalls(this);
2028
2029 Reference expr = expression(ast->base);
2030 Reference base = expr;
2031
2032 if (hasError())
2033 return false;
2034 switch (base.type) {
2035 case Reference::Member:
2036 base = base.asLValue();
2037 break;
2038 case Reference::Subscript:
2039 base.element = loadSubscriptForCall(base).storeOnStack().stackSlot();
2040 base.subscriptLoadedForCall = true;
2041 break;
2042 case Reference::Name:
2043 break;
2044 case Reference::Super:
2045 handleConstruct(base, ast->arguments);
2046 return false;
2047 case Reference::SuperProperty:
2048 break;
2049 default:
2050 base = base.storeOnStack();
2051 break;
2052 }
2053
2054 if (expr.hasSavedCallBaseSlot) {
2055 // Hack to preserve `this` context in optional chain calls. See optionalChainFinalizer().
2056 base.hasSavedCallBaseSlot = true;
2057 base.savedCallBaseSlot = expr.savedCallBaseSlot;
2058 base.savedCallPropertyNameIndex = expr.savedCallPropertyNameIndex;
2059 }
2060
2061 int thisObject = bytecodeGenerator->newRegister();
2062 int functionObject = bytecodeGenerator->newRegister();
2063
2064 if (ast->isOptional || m_optionalChainsStates.top().actuallyHasOptionals) {
2065 base.loadInAccumulator();
2066 bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
2067 auto jumpToUndefined = bytecodeGenerator->jumpTrue();
2068 m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
2069 }
2070
2071 auto calldata = pushArgs(ast->arguments);
2072 if (hasError())
2073 return false;
2074
2075 blockTailCalls.unblock();
2076 if (calldata.hasSpread || _tailCallsAreAllowed) {
2077 Reference baseObject = base.baseObject();
2078 if (!baseObject.isStackSlot()) {
2079 baseObject.storeOnStack(thisObject);
2080 baseObject = Reference::fromStackSlot(this, thisObject);
2081 }
2082
2083 const int func = [&]() {
2084 if (base.type == Reference::Subscript)
2085 return base.element;
2086
2087 if (!base.isStackSlot()) {
2088 base.storeOnStack(functionObject);
2089 base = Reference::fromStackSlot(this, functionObject);
2090 }
2091
2092 return base.stackSlot();
2093 }();
2094
2095 if (calldata.hasSpread) {
2096 Instruction::CallWithSpread call;
2097 call.func = func;
2098 call.thisObject = baseObject.stackSlot();
2099 call.argc = calldata.argc;
2100 call.argv = calldata.argv;
2101 bytecodeGenerator->addInstruction(call);
2102 } else {
2103 Instruction::TailCall call;
2104 call.func = func;
2105 call.thisObject = baseObject.stackSlot();
2106 call.argc = calldata.argc;
2107 call.argv = calldata.argv;
2108 bytecodeGenerator->addInstruction(call);
2109 }
2110
2111 optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain);
2112 return false;
2113 }
2114
2115 handleCall(base, calldata, functionObject, thisObject, ast->isOptional);
2116 optionalChainFinalizer(Reference::fromAccumulator(this), isTailOfChain);
2117 return false;
2118}
2119
2120void Codegen::endVisit(CallExpression *ast)
2121{
2122 m_seenOptionalChainNodes.remove(ast);
2123}
2124
2125void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject, bool optional)
2126{
2127 if (base.sourceLocation.isValid())
2128 bytecodeGenerator->setLocation(base.sourceLocation);
2129
2130 //### Do we really need all these call instructions? can's we load the callee in a temp?
2131 if (base.type == Reference::Member || base.hasSavedCallBaseSlot) {
2132 if (useFastLookups) {
2133 Instruction::CallPropertyLookup call;
2134 if (base.hasSavedCallBaseSlot) {
2135 call.base = base.savedCallBaseSlot;
2136 call.lookupIndex = registerGetterLookup(
2137 base.savedCallPropertyNameIndex, JSUnitGenerator::LookupForCall);
2138 } else {
2139 call.base = base.propertyBase.stackSlot();
2140 call.lookupIndex = registerGetterLookup(
2141 base.propertyNameIndex, JSUnitGenerator::LookupForCall);
2142 }
2143 call.argc = calldata.argc;
2144 call.argv = calldata.argv;
2145 bytecodeGenerator->addInstruction(call);
2146 } else {
2147 Instruction::CallProperty call;
2148 if (base.hasSavedCallBaseSlot) {
2149 call.base = base.savedCallBaseSlot;
2150 call.name = base.savedCallPropertyNameIndex;
2151 } else {
2152 call.base = base.propertyBase.stackSlot();
2153 call.name = base.propertyNameIndex;
2154 }
2155 call.argc = calldata.argc;
2156 call.argv = calldata.argv;
2157 bytecodeGenerator->addInstruction(call);
2158 }
2159 } else if (base.type == Reference::Subscript) {
2160 Instruction::CallWithReceiver call;
2161 call.thisObject = base.elementBase.stackSlot();
2162 call.name = base.element;
2163 call.argc = calldata.argc;
2164 call.argv = calldata.argv;
2165 bytecodeGenerator->addInstruction(call);
2166 } else if (base.type == Reference::Name) {
2167 if (base.name == QStringLiteral("eval") && !optional) {
2168 Instruction::CallPossiblyDirectEval call;
2169 call.argc = calldata.argc;
2170 call.argv = calldata.argv;
2171 bytecodeGenerator->addInstruction(call);
2172 } else if (useFastLookups && base.global) {
2173 if (base.qmlGlobal) {
2174 Instruction::CallQmlContextPropertyLookup call;
2175 call.index = registerQmlContextPropertyGetterLookup(
2176 base.nameAsIndex(), JSUnitGenerator::LookupForCall);
2177 call.argc = calldata.argc;
2178 call.argv = calldata.argv;
2179 bytecodeGenerator->addInstruction(call);
2180 } else {
2181 Instruction::CallGlobalLookup call;
2182 call.index = registerGlobalGetterLookup(
2183 base.nameAsIndex(), JSUnitGenerator::LookupForCall);
2184 call.argc = calldata.argc;
2185 call.argv = calldata.argv;
2186 bytecodeGenerator->addInstruction(call);
2187 }
2188 } else {
2189 Instruction::CallName call;
2190 call.name = base.nameAsIndex();
2191 call.argc = calldata.argc;
2192 call.argv = calldata.argv;
2193 bytecodeGenerator->addInstruction(call);
2194 }
2195 } else if (base.type == Reference::SuperProperty) {
2196 Reference receiver = base.baseObject();
2197 if (!base.isStackSlot()) {
2198 base.storeOnStack(slotForFunction);
2199 base = Reference::fromStackSlot(this, slotForFunction);
2200 }
2201 if (!receiver.isStackSlot()) {
2202 receiver.storeOnStack(slotForThisObject);
2203 receiver = Reference::fromStackSlot(this, slotForThisObject);
2204 }
2205 Instruction::CallWithReceiver call;
2206 call.name = base.stackSlot();
2207 call.thisObject = receiver.stackSlot();
2208 call.argc = calldata.argc;
2209 call.argv = calldata.argv;
2210 bytecodeGenerator->addInstruction(call);
2211 } else {
2212 Q_ASSERT(base.isStackSlot());
2213 Instruction::CallValue call;
2214 call.name = base.stackSlot();
2215 call.argc = calldata.argc;
2216 call.argv = calldata.argv;
2217 bytecodeGenerator->addInstruction(call);
2218 }
2219}
2220
2221Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
2222{
2223 bool hasSpread = false;
2224 int argc = 0;
2225 for (ArgumentList *it = args; it; it = it->next) {
2226 if (it->isSpreadElement) {
2227 hasSpread = true;
2228 ++argc;
2229 }
2230 ++argc;
2231 }
2232
2233 if (!argc)
2234 return { 0, 0, false };
2235
2236 int calldata = bytecodeGenerator->newRegisterArray(argc);
2237
2238 argc = 0;
2239 for (ArgumentList *it = args; it; it = it->next) {
2240 if (it->isSpreadElement) {
2241 Reference::fromConst(
2242 this,
2243 StaticValue::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
2244 ++argc;
2245 }
2246 RegisterScope scope(this);
2247 Reference e = expression(it->expression);
2248 if (hasError())
2249 break;
2250 if (!argc && !it->next && !hasSpread) {
2251 // avoid copy for functions taking a single argument
2252 if (e.isStackSlot()) {
2253 e.tdzCheck();
2254 return { 1, e.stackSlot(), hasSpread };
2255 }
2256 }
2257 (void) e.storeOnStack(calldata + argc);
2258 ++argc;
2259 }
2260
2261 return { argc, calldata, hasSpread };
2262}
2263
2264Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
2265{
2266 int argc = 0;
2267 for (TemplateLiteral *it = args; it; it = it->next)
2268 ++argc;
2269
2270 if (!argc)
2271 return { 0, 0, false };
2272
2273 int calldata = bytecodeGenerator->newRegisterArray(argc);
2274
2275 argc = 0;
2276 for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
2277 RegisterScope scope(this);
2278 Reference e = expression(it->expression);
2279 if (hasError())
2280 break;
2281 (void) e.storeOnStack(calldata + argc);
2282 ++argc;
2283 }
2284
2285 return { argc, calldata, false };
2286}
2287
2288bool Codegen::visit(ConditionalExpression *ast)
2289{
2290 if (hasError())
2291 return false;
2292
2293 RegisterScope scope(this);
2294 TailCallBlocker blockTailCalls(this);
2295
2296 BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
2297 BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel();
2298 condition(ast->expression, &iftrue, &iffalse, true);
2299
2300 blockTailCalls.unblock();
2301
2302 iftrue.link();
2303 Reference ok = expression(ast->ok);
2304 if (hasError())
2305 return false;
2306 ok.loadInAccumulator();
2307 BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
2308
2309 iffalse.link();
2310 Reference ko = expression(ast->ko);
2311 if (hasError()) {
2312 jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering
2313 return false;
2314 }
2315 ko.loadInAccumulator();
2316
2317 jump_endif.link();
2318 setExprResult(Reference::fromAccumulator(this));
2319
2320 return false;
2321}
2322
2323bool Codegen::visit(DeleteExpression *ast)
2324{
2325 if (hasError())
2326 return false;
2327
2328 const bool isTailOfChain = traverseOptionalChain(ast);
2329
2330 RegisterScope scope(this);
2331 TailCallBlocker blockTailCalls(this);
2332 Reference expr = expression(ast->expression);
2333 if (hasError())
2334 return false;
2335
2336 const bool chainActuallyHasOptionals = m_optionalChainsStates.top().actuallyHasOptionals;
2337 if (chainActuallyHasOptionals)
2338 Q_ASSERT(expr.type == Reference::Member || expr.type == Reference::Subscript);
2339
2340 switch (expr.type) {
2341 case Reference::SuperProperty:
2342 // ### this should throw a reference error at runtime.
2343 return false;
2344 case Reference::StackSlot:
2345 if (!expr.stackSlotIsLocalOrArgument)
2346 break;
2347 Q_FALLTHROUGH();
2348 case Reference::ScopedLocal:
2349 // Trying to delete a function argument might throw.
2350 if (_context->isStrict) {
2351 throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
2352 return false;
2353 }
2354 setExprResult(Reference::fromConst(this, QV4::Encode(false)));
2355 return false;
2356 case Reference::Name: {
2357 if (_context->isStrict) {
2358 throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
2359 return false;
2360 }
2361 Instruction::DeleteName del;
2362 del.name = expr.nameAsIndex();
2363 bytecodeGenerator->addInstruction(del);
2364 setExprResult(Reference::fromAccumulator(this));
2365 return false;
2366 }
2367 case Reference::Member: {
2368 //### maybe add a variant where the base can be in the accumulator?
2369 expr = expr.asLValue();
2370
2371 if (chainActuallyHasOptionals) {
2372 expr.loadInAccumulator();
2373 bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
2374 auto jumpToUndefined = bytecodeGenerator->jumpTrue();
2375 m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
2376 }
2377
2378 Instruction::LoadRuntimeString instr;
2379 instr.stringId = expr.propertyNameIndex;
2380 bytecodeGenerator->addInstruction(instr);
2381 Reference index = Reference::fromStackSlot(this);
2382 index.storeConsumeAccumulator();
2383 Instruction::DeleteProperty del;
2384 del.base = expr.propertyBase.stackSlot();
2385 del.index = index.stackSlot();
2386 bytecodeGenerator->addInstruction(del);
2387 auto ref = Reference::fromAccumulator(this);
2388
2389 optionalChainFinalizer(ref, isTailOfChain, true);
2390 return false;
2391 }
2392 case Reference::Subscript: {
2393 //### maybe add a variant where the index can be in the accumulator?
2394 expr = expr.asLValue();
2395
2396 if (chainActuallyHasOptionals) {
2397 expr.loadInAccumulator();
2398 bytecodeGenerator->addInstruction(Instruction::CmpEqNull());
2399 auto jumpToUndefined = bytecodeGenerator->jumpTrue();
2400 m_optionalChainsStates.top().jumpsToPatch.emplace_back(std::move(jumpToUndefined));
2401 }
2402
2403 Instruction::DeleteProperty del;
2404 del.base = expr.elementBase;
2405 del.index = expr.elementSubscript.stackSlot();
2406 bytecodeGenerator->addInstruction(del);
2407 auto ref = Reference::fromAccumulator(this);
2408
2409 optionalChainFinalizer(ref, isTailOfChain, true);
2410 return false;
2411 }
2412 default:
2413 break;
2414 }
2415 // [[11.4.1]] Return true if it's not a reference
2416 setExprResult(Reference::fromConst(this, QV4::Encode(true)));
2417 return false;
2418}
2419
2420void Codegen::endVisit(DeleteExpression *ast) {
2421 m_seenOptionalChainNodes.remove(ast);
2422}
2423
2424bool Codegen::visit(FalseLiteral *)
2425{
2426 if (hasError())
2427 return false;
2428
2429 setExprResult(Reference::fromConst(this, QV4::Encode(false)));
2430 return false;
2431}
2432
2433bool Codegen::visit(SuperLiteral *)
2434{
2435 if (hasError())
2436 return false;
2437
2438 setExprResult(Reference::fromSuper(this));
2439 return false;
2440}
2441
2442bool Codegen::traverseOptionalChain(Node *node)
2443{
2444 if (m_seenOptionalChainNodes.contains(node))
2445 return false;
2446
2447 const auto isOptionalChainableNode = [](const Node *node) {
2448 return node->kind == Node::Kind_FieldMemberExpression ||
2449 node->kind == Node::Kind_CallExpression ||
2450 node->kind == Node::Kind_ArrayMemberExpression ||
2451 node->kind == Node::Kind_DeleteExpression;
2452 };
2453 m_optionalChainsStates.emplace();
2454 while (isOptionalChainableNode(node)) {
2455 m_seenOptionalChainNodes.insert(node);
2456
2457 switch (node->kind) {
2458 case Node::Kind_FieldMemberExpression: {
2459 auto *fme = AST::cast<FieldMemberExpression *>(node);
2460 m_optionalChainsStates.top().actuallyHasOptionals |= fme->isOptional;
2461 node = fme->base;
2462 break;
2463 }
2464 case Node::Kind_CallExpression: {
2465 auto *ce = AST::cast<CallExpression *>(node);
2466 m_optionalChainsStates.top().actuallyHasOptionals |= ce->isOptional;
2467 node = ce->base;
2468 break;
2469 }
2470 case Node::Kind_ArrayMemberExpression: {
2471 auto *ame = AST::cast<ArrayMemberExpression *>(node);
2472 m_optionalChainsStates.top().actuallyHasOptionals |= ame->isOptional;
2473 node = ame->base;
2474 break;
2475 }
2476 case Node::Kind_DeleteExpression:
2477 node = AST::cast<DeleteExpression *>(node)->expression;
2478 break;
2479 default:
2480 Q_UNREACHABLE();
2481 }
2482 }
2483
2484 return true;
2485}
2486
2487void Codegen::optionalChainFinalizer(const Reference &expressionResult, bool tailOfChain,
2488 bool isDeleteExpression)
2489{
2490 auto &chainState = m_optionalChainsStates.top();
2491 if (!tailOfChain) {
2492 setExprResult(expressionResult);
2493 return;
2494 } else if (!chainState.actuallyHasOptionals) {
2495 setExprResult(expressionResult);
2496 m_optionalChainsStates.pop();
2497 return;
2498 }
2499
2500 auto savedBaseSlot = -1;
2501 if (expressionResult.type == Reference::Member)
2502 savedBaseSlot = expressionResult.propertyBase.storeOnStack().stackSlot();
2503 expressionResult.loadInAccumulator();
2504
2505 std::optional<Moth::BytecodeGenerator::Jump> jumpToDone;
2506 if (!isDeleteExpression) // Delete expressions always return true, avoid the extra jump
2507 jumpToDone.emplace(bytecodeGenerator->jump());
2508
2509 for (auto &jump : chainState.jumpsToPatch)
2510 jump.link();
2511
2512 if (isDeleteExpression)
2513 bytecodeGenerator->addInstruction(Instruction::LoadTrue());
2514 else
2515 bytecodeGenerator->addInstruction(Instruction::LoadUndefined());
2516
2517 if (jumpToDone.has_value())
2518 jumpToDone.value().link();
2519
2520 auto ref = Reference::fromAccumulator(this);
2521 if (expressionResult.type == Reference::Member) {
2522 /* Because the whole optional chain is handled at once with a chain finalizer instead of
2523 * instruction by instruction, the result of the chain (either undefined or the result of
2524 * the optional operation) is stored in the accumulator. This works fine except for one
2525 * edge case where the `this` context is required in a call
2526 * (see tst_ecmascripttests: language/expressions/optional-chaining/optional-call-preserves-this.js).
2527 *
2528 * In order to preserve the `this` context in the call, the call base and the property name
2529 * index need to be available as with a Member reference. However, since the result must be
2530 * in the accumulator the resulting reference is of type Accumulator. Therefore, the call
2531 * base and the property name index are `glued` to an accumulator reference to make it work
2532 * when deciding which call instruction to use later on.
2533 */
2534 ref.hasSavedCallBaseSlot = true;
2535 ref.savedCallBaseSlot = savedBaseSlot;
2536 ref.savedCallPropertyNameIndex = expressionResult.propertyNameIndex;
2537 }
2538 setExprResult(ref);
2539 m_optionalChainsStates.pop();
2540}
2541
2542bool Codegen::visit(FieldMemberExpression *ast)
2543{
2544 if (hasError())
2545 return false;
2546
2547 const bool isTailOfChain = traverseOptionalChain(ast);
2548
2549 TailCallBlocker blockTailCalls(this);
2550
2551 if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
2552 if (id->name == QLatin1String("new")) {
2553 // new.target
2554 Q_ASSERT(ast->name == QLatin1String("target"));
2555
2556 if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
2557 Reference r = referenceForName(QStringLiteral("new.target"), false);
2558 r.isReadonly = true;
2559 setExprResult(r);
2560
2561 return false;
2562 }
2563
2564 auto ref = Reference::fromStackSlot(this, CallData::NewTarget);
2565 optionalChainFinalizer(ref, isTailOfChain);
2566 return false;
2567 }
2568 }
2569
2570 Reference base = expression(ast->base);
2571
2572 if (hasError())
2573 return false;
2574 if (base.isSuper()) {
2575 Instruction::LoadRuntimeString load;
2576 load.stringId = registerString(ast->name.toString());
2577 bytecodeGenerator->addInstruction(load);
2578 Reference property = Reference::fromAccumulator(this).storeOnStack();
2579
2580 optionalChainFinalizer(Reference::fromSuperProperty(property), isTailOfChain);
2581 return false;
2582 }
2583
2584 auto ref = Reference::fromMember(base, ast->name.toString(), ast->lastSourceLocation(),
2585 ast->isOptional, &m_optionalChainsStates.top().jumpsToPatch);
2586
2587 optionalChainFinalizer(ref, isTailOfChain);
2588 return false;
2589}
2590
2591void Codegen::endVisit(FieldMemberExpression *ast)
2592{
2593 m_seenOptionalChainNodes.remove(ast);
2594}
2595
2596bool Codegen::visit(TaggedTemplate *ast)
2597{
2598 if (hasError())
2599 return false;
2600
2601 RegisterScope scope(this);
2602 return handleTaggedTemplate(expression(ast->base), ast);
2603}
2604
2605bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
2606{
2607 if (hasError())
2608 return false;
2609
2610 int functionObject = -1, thisObject = -1;
2611 switch (base.type) {
2612 case Reference::Member:
2613 base = base.asLValue();
2614 break;
2615 case Reference::Subscript:
2616 base.element = loadSubscriptForCall(base).storeOnStack().stackSlot();
2617 base.subscriptLoadedForCall = true;
2618 break;
2619 case Reference::Name:
2620 break;
2621 case Reference::SuperProperty:
2622 thisObject = bytecodeGenerator->newRegister();
2623 functionObject = bytecodeGenerator->newRegister();
2624 break;
2625 default:
2626 base = base.storeOnStack();
2627 break;
2628 }
2629
2630 createTemplateObject(ast->templateLiteral);
2631 int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot();
2632 Q_UNUSED(templateObjectTemp);
2633 auto calldata = pushTemplateArgs(ast->templateLiteral);
2634 if (hasError())
2635 return false;
2636 ++calldata.argc;
2637 Q_ASSERT(calldata.argv == templateObjectTemp + 1);
2638 --calldata.argv;
2639
2640 handleCall(base, calldata, functionObject, thisObject);
2641 setExprResult(Reference::fromAccumulator(this));
2642 return false;
2643}
2644
2645void Codegen::createTemplateObject(TemplateLiteral *t)
2646{
2647 TemplateObject obj;
2648
2649 for (TemplateLiteral *it = t; it; it = it->next) {
2650 obj.strings.append(registerString(it->value.toString()));
2651 obj.rawStrings.append(registerString(it->rawValue.toString()));
2652 }
2653
2654 int index = _module->templateObjects.size();
2655 _module->templateObjects.append(obj);
2656
2657 Instruction::GetTemplateObject getTemplateObject;
2658 getTemplateObject.index = index;
2659 bytecodeGenerator->addInstruction(getTemplateObject);
2660}
2661
2662bool Codegen::visit(FunctionExpression *ast)
2663{
2664 if (hasError())
2665 return false;
2666
2667 TailCallBlocker blockTailCalls(this);
2668
2669 RegisterScope scope(this);
2670
2671 int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
2672 if (hasError())
2673 return false;
2674 loadClosure(function);
2675 setExprResult(Reference::fromAccumulator(this));
2676 return false;
2677}
2678
2679Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, const SourceLocation &accessLocation)
2680{
2681 Context::ResolvedName resolved = _context->resolveName(name, accessLocation);
2682 bool throwsReferenceError = false;
2683
2684 if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack
2685 || resolved.type == Context::ResolvedName::Import) {
2686 if (resolved.isArgOrEval && isLhs)
2687 // ### add correct source location
2688 throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
2689
2690 if (resolved.declarationLocation.isValid() && accessLocation.isValid()
2691 && resolved.declarationLocation.begin() > accessLocation.end()) {
2692 Q_ASSERT(_interface);
2693 if (resolved.memberType == Context::FunctionDefinition) {
2694 _interface->reportFunctionUsedBeforeDeclaration(
2695 name, url().toLocalFile(), resolved.declarationLocation, accessLocation);
2696 } else {
2697 _interface->reportVarUsedBeforeDeclaration(
2698 name, url().toLocalFile(), resolved.declarationLocation, accessLocation);
2699 }
2700 if (resolved.type == Context::ResolvedName::Stack && resolved.requiresTDZCheck)
2701 throwsReferenceError = true;
2702 }
2703
2704 if (resolved.isInjected && accessLocation.isValid()) {
2705 qCWarning(lcQmlInjectedParameter).nospace().noquote()
2706 << url().toString() << ":" << accessLocation.startLine
2707 << ":" << accessLocation.startColumn << " Parameter \"" << name
2708 << "\" is not declared."
2709 << " Injection of parameters into signal handlers is deprecated."
2710 << " Use JavaScript functions with formal parameters instead.";
2711 }
2712
2713 Reference r;
2714 switch (resolved.type) {
2715 case Context::ResolvedName::Local:
2716 r = Reference::fromScopedLocal(this, resolved.index, resolved.scope); break;
2717 case Context::ResolvedName::Stack:
2718 r = Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); break;
2719 case Context::ResolvedName::Import:
2720 r = Reference::fromImport(this, resolved.index); break;
2721 default: Q_UNREACHABLE();
2722 }
2723 if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name))
2724 r.isVolatile = true;
2725 r.isArgOrEval = resolved.isArgOrEval;
2726 r.isReferenceToConst = resolved.isConst;
2727 r.requiresTDZCheck = resolved.requiresTDZCheck;
2728 r.name = name; // used to show correct name at run-time when TDZ check fails.
2729 r.sourceLocation = accessLocation;
2730 r.throwsReferenceError = throwsReferenceError;
2731 return r;
2732 }
2733
2734 Reference r = Reference::fromName(this, name);
2735 r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal);
2736 r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal;
2737 r.sourceLocation = accessLocation;
2738 if (!r.global && !r.qmlGlobal
2739 && _context->contextType == ContextType::ScriptImportedByQML
2740 && Codegen::isNameGlobal(name)) {
2741 // the global object is frozen, so we know that members of it are
2742 // pointing to the global object. This is important so that references
2743 // to Math etc. do not go through the expensive path in the context wrapper
2744 // that tries to see whether we have a matching type
2745 r.global = true;
2746 }
2747 return r;
2748}
2749
2750void Codegen::loadClosure(int closureId)
2751{
2752 if (closureId >= 0) {
2753 Instruction::LoadClosure load;
2754 load.value = closureId;
2755 bytecodeGenerator->addInstruction(load);
2756 } else {
2757 Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
2758 }
2759}
2760
2761bool Codegen::visit(IdentifierExpression *ast)
2762{
2763 if (hasError())
2764 return false;
2765
2766 setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
2767 return false;
2768}
2769
2770bool Codegen::visit(NestedExpression *ast)
2771{
2772 if (hasError())
2773 return false;
2774
2775 accept(ast->expression);
2776 return false;
2777}
2778
2779void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
2780{
2781 Reference constructor;
2782 if (base.isSuper()) {
2783 Instruction::LoadSuperConstructor super;
2784 bytecodeGenerator->addInstruction(super);
2785 constructor = Reference::fromAccumulator(this).storeOnStack();
2786 } else {
2787 constructor = base.storeOnStack();
2788 }
2789
2790 auto calldata = pushArgs(arguments);
2791 if (hasError())
2792 return;
2793
2794 if (base.isSuper())
2795 Reference::fromStackSlot(this, CallData::NewTarget).loadInAccumulator();
2796 else
2797 constructor.loadInAccumulator();
2798
2799 if (calldata.hasSpread) {
2800 Instruction::ConstructWithSpread create;
2801 create.func = constructor.stackSlot();
2802 create.argc = calldata.argc;
2803 create.argv = calldata.argv;
2804 bytecodeGenerator->addInstruction(create);
2805 } else {
2806 Instruction::Construct create;
2807 create.func = constructor.stackSlot();
2808 create.argc = calldata.argc;
2809 create.argv = calldata.argv;
2810 bytecodeGenerator->addInstruction(create);
2811 }
2812 if (base.isSuper())
2813 // set the result up as the thisObject
2814 Reference::fromAccumulator(this).storeOnStack(CallData::This);
2815
2816 setExprResult(Reference::fromAccumulator(this));
2817}
2818
2819bool Codegen::visit(NewExpression *ast)
2820{
2821 if (hasError())
2822 return false;
2823
2824 RegisterScope scope(this);
2825 TailCallBlocker blockTailCalls(this);
2826
2827 Reference base = expression(ast->expression);
2828 if (hasError())
2829 return false;
2830 if (base.isSuper()) {
2831 throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
2832 return false;
2833 }
2834
2835 handleConstruct(base, nullptr);
2836 return false;
2837}
2838
2839bool Codegen::visit(NewMemberExpression *ast)
2840{
2841 if (hasError())
2842 return false;
2843
2844 RegisterScope scope(this);
2845 TailCallBlocker blockTailCalls(this);
2846
2847 Reference base = expression(ast->base);
2848 if (hasError())
2849 return false;
2850 if (base.isSuper()) {
2851 throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
2852 return false;
2853 }
2854
2855 handleConstruct(base, ast->arguments);
2856 return false;
2857}
2858
2859bool Codegen::visit(NotExpression *ast)
2860{
2861 if (hasError())
2862 return false;
2863
2864 TailCallBlocker blockTailCalls(this);
2865 setExprResult(unop(Not, expression(ast->expression)));
2866 return false;
2867}
2868
2869bool Codegen::visit(NullExpression *)
2870{
2871 if (hasError())
2872 return false;
2873
2874 if (exprAccept(cx))
2875 bytecodeGenerator->jump().link(*currentExpr().iffalse());
2876 else
2877 setExprResult(Reference::fromConst(this, Encode::null()));
2878
2879 return false;
2880}
2881
2882bool Codegen::visit(NumericLiteral *ast)
2883{
2884 if (hasError())
2885 return false;
2886
2887 setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
2888 return false;
2889}
2890
2891bool Codegen::visit(ObjectPattern *ast)
2892{
2893 if (hasError())
2894 return false;
2895
2896 TailCallBlocker blockTailCalls(this);
2897
2898 RegisterScope scope(this);
2899
2900 QStringList members;
2901
2902 int argc = 0;
2903 int args = 0;
2904 auto push = [this, &args, &argc](const Reference &arg) {
2905 int temp = bytecodeGenerator->newRegister();
2906 if (argc == 0)
2907 args = temp;
2908 (void) arg.storeOnStack(temp);
2909 ++argc;
2910 };
2911
2912 PatternPropertyList *it = ast->properties;
2913 for (; it; it = it->next) {
2914 PatternProperty *p = it->property;
2915 AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
2916 if (cname || p->type != PatternProperty::Literal)
2917 break;
2918 QString name = p->name->asString();
2919 uint arrayIndex = stringToArrayIndex(name);
2920 if (arrayIndex != UINT_MAX)
2921 break;
2922 if (members.contains(name))
2923 break;
2924 members.append(name);
2925
2926 {
2927 RegisterScope innerScope(this);
2928 Reference value = expression(p->initializer, name);
2929 if (hasError())
2930 return false;
2931 value.loadInAccumulator();
2932 }
2933 push(Reference::fromAccumulator(this));
2934 }
2935
2936 int classId = jsUnitGenerator->registerJSClass(members);
2937
2938 // handle complex property setters
2939 for (; it; it = it->next) {
2940 PatternProperty *p = it->property;
2941 AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
2942 ObjectLiteralArgument argType = ObjectLiteralArgument::Value;
2943 if (p->type == PatternProperty::Method)
2944 argType = ObjectLiteralArgument::Method;
2945 else if (p->type == PatternProperty::Getter)
2946 argType = ObjectLiteralArgument::Getter;
2947 else if (p->type == PatternProperty::Setter)
2948 argType = ObjectLiteralArgument::Setter;
2949
2950 Reference::fromConst(this, Encode(int(argType))).loadInAccumulator();
2951 push(Reference::fromAccumulator(this));
2952
2953 if (cname) {
2954 RegisterScope innerScope(this);
2955 Reference name = expression(cname->expression);
2956 if (hasError())
2957 return false;
2958 name.loadInAccumulator();
2959 } else {
2960 QString name = p->name->asString();
2961#if 0
2962 uint arrayIndex = QV4::String::toArrayIndex(name);
2963 if (arrayIndex != UINT_MAX) {
2964 Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator();
2965 } else
2966#endif
2967 {
2968 Instruction::LoadRuntimeString instr;
2969 instr.stringId = registerString(name);
2970 bytecodeGenerator->addInstruction(instr);
2971 }
2972 }
2973 push(Reference::fromAccumulator(this));
2974 {
2975 RegisterScope innerScope(this);
2976 if (p->type != PatternProperty::Literal) {
2977 // need to get the closure id for the method
2978 FunctionExpression *f = p->initializer->asFunctionDefinition();
2979 Q_ASSERT(f);
2980 int function = defineFunction(f->name.toString(), f, f->formals, f->body);
2981 if (hasError())
2982 return false;
2983 Reference::fromConst(this, Encode(function)).loadInAccumulator();
2984 } else {
2985 Reference value = expression(p->initializer);
2986 if (hasError())
2987 return false;
2988 value.loadInAccumulator();
2989 }
2990 }
2991 push(Reference::fromAccumulator(this));
2992 }
2993
2994 Instruction::DefineObjectLiteral call;
2995 call.internalClassId = classId;
2996 call.argc = argc;
2997 call.args = Moth::StackSlot::createRegister(args);
2998 bytecodeGenerator->addInstruction(call);
2999 setExprResult(Reference::fromAccumulator(this));
3000 return false;
3001}
3002
3003bool Codegen::visit(PostDecrementExpression *ast)
3004{
3005 if (hasError())
3006 return false;
3007
3008 Reference expr = expression(ast->base);
3009 if (hasError())
3010 return false;
3011 if (!expr.isLValue()) {
3012 throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
3013 return false;
3014 }
3015 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
3016 return false;
3017
3018 setExprResult(unop(PostDecrement, expr));
3019
3020 return false;
3021}
3022
3023bool Codegen::visit(PostIncrementExpression *ast)
3024{
3025 if (hasError())
3026 return false;
3027
3028 Reference expr = expression(ast->base);
3029 if (hasError())
3030 return false;
3031 if (!expr.isLValue()) {
3032 throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation"));
3033 return false;
3034 }
3035 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
3036 return false;
3037
3038 setExprResult(unop(PostIncrement, expr));
3039 return false;
3040}
3041
3042bool Codegen::visit(PreDecrementExpression *ast)
3043{ if (hasError())
3044 return false;
3045
3046 Reference expr = expression(ast->expression);
3047 if (hasError())
3048 return false;
3049 if (!expr.isLValue()) {
3050 throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
3051 return false;
3052 }
3053
3054 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
3055 return false;
3056 setExprResult(unop(PreDecrement, expr));
3057 return false;
3058}
3059
3060bool Codegen::visit(PreIncrementExpression *ast)
3061{
3062 if (hasError())
3063 return false;
3064
3065 Reference expr = expression(ast->expression);
3066 if (hasError())
3067 return false;
3068 if (!expr.isLValue()) {
3069 throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference."));
3070 return false;
3071 }
3072
3073 if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
3074 return false;
3075 setExprResult(unop(PreIncrement, expr));
3076 return false;
3077}
3078
3079bool Codegen::visit(RegExpLiteral *ast)
3080{
3081 if (hasError())
3082 return false;
3083
3084 auto r = Reference::fromStackSlot(this);
3085 r.isReadonly = true;
3086 setExprResult(r);
3087
3088 Instruction::MoveRegExp instr;
3089 instr.regExpId = jsUnitGenerator->registerRegExp(ast);
3090 instr.destReg = r.stackSlot();
3091 bytecodeGenerator->addInstruction(instr);
3092 return false;
3093}
3094
3095bool Codegen::visit(StringLiteral *ast)
3096{
3097 if (hasError())
3098 return false;
3099
3100 auto r = Reference::fromAccumulator(this);
3101 r.isReadonly = true;
3102 setExprResult(r);
3103
3104 Instruction::LoadRuntimeString instr;
3105 instr.stringId = registerString(ast->value.toString());
3106 bytecodeGenerator->addInstruction(instr);
3107 return false;
3108}
3109
3110bool Codegen::visit(TemplateLiteral *ast)
3111{
3112 if (hasError())
3113 return false;
3114
3115 TailCallBlocker blockTailCalls(this);
3116
3117 Instruction::LoadRuntimeString instr;
3118 instr.stringId = registerString(ast->value.toString());
3119 bytecodeGenerator->addInstruction(instr);
3120
3121 if (ast->expression) {
3122 RegisterScope scope(this);
3123 int temp = bytecodeGenerator->newRegister();
3124 Instruction::StoreReg store;
3125 store.reg = temp;
3126 bytecodeGenerator->addInstruction(store);
3127
3128 Reference expr = expression(ast->expression);
3129 if (hasError())
3130 return false;
3131
3132 if (ast->next) {
3133 int temp2 = bytecodeGenerator->newRegister();
3134 expr.storeOnStack(temp2);
3135 visit(ast->next);
3136
3137 Instruction::Add instr;
3138 instr.lhs = temp2;
3139 bytecodeGenerator->addInstruction(instr);
3140 } else {
3141 expr.loadInAccumulator();
3142 }
3143
3144 Instruction::Add instr;
3145 instr.lhs = temp;
3146 bytecodeGenerator->addInstruction(instr);
3147 }
3148
3149 auto r = Reference::fromAccumulator(this);
3150 r.isReadonly = true;
3151
3152 setExprResult(r);
3153 return false;
3154
3155}
3156
3157bool Codegen::visit(ThisExpression *)
3158{
3159 if (hasError())
3160 return false;
3161
3162 for (Context *parentContext = _context; parentContext; parentContext = parentContext->parent) {
3163 if (parentContext->isArrowFunction) {
3164 Reference r = referenceForName(QStringLiteral("this"), false);
3165 r.isReadonly = true;
3166 setExprResult(r);
3167 return false;
3168 }
3169 if (parentContext->contextType != ContextType::Block)
3170 break;
3171 }
3172
3173 setExprResult(Reference::fromThis(this));
3174 return false;
3175}
3176
3177bool Codegen::visit(TildeExpression *ast)
3178{
3179 if (hasError())
3180 return false;
3181
3182 TailCallBlocker blockTailCalls(this);
3183 setExprResult(unop(Compl, expression(ast->expression)));
3184 return false;
3185}
3186
3187bool Codegen::visit(TrueLiteral *)
3188{
3189 if (hasError())
3190 return false;
3191
3192 setExprResult(Reference::fromConst(this, QV4::Encode(true)));
3193 return false;
3194}
3195
3196bool Codegen::visit(TypeOfExpression *ast)
3197{
3198 if (hasError())
3199 return false;
3200
3201 RegisterScope scope(this);
3202 TailCallBlocker blockTailCalls(this);
3203
3204 Reference expr = expression(ast->expression);
3205 if (hasError())
3206 return false;
3207
3208 if (expr.type == Reference::Name) {
3209 // special handling as typeof doesn't throw here
3210 Instruction::TypeofName instr;
3211 instr.name = expr.nameAsIndex();
3212 bytecodeGenerator->addInstruction(instr);
3213 } else {
3214 expr.loadInAccumulator();
3215 Instruction::TypeofValue instr;
3216 bytecodeGenerator->addInstruction(instr);
3217 }
3218 setExprResult(Reference::fromAccumulator(this));
3219
3220 return false;
3221}
3222
3223bool Codegen::visit(UnaryMinusExpression *ast)
3224{
3225 if (hasError())
3226 return false;
3227
3228 TailCallBlocker blockTailCalls(this);
3229 setExprResult(unop(UMinus, expression(ast->expression)));
3230 return false;
3231}
3232
3233bool Codegen::visit(UnaryPlusExpression *ast)
3234{
3235 if (hasError())
3236 return false;
3237
3238 TailCallBlocker blockTailCalls(this);
3239 setExprResult(unop(UPlus, expression(ast->expression)));
3240 return false;
3241}
3242
3243bool Codegen::visit(VoidExpression *ast)
3244{
3245 if (hasError())
3246 return false;
3247
3248 RegisterScope scope(this);
3249 TailCallBlocker blockTailCalls(this);
3250
3251 statement(ast->expression);
3252 setExprResult(Reference::fromConst(this, Encode::undefined()));
3253 return false;
3254}
3255
3256bool Codegen::visit(FunctionDeclaration * ast)
3257{
3258 if (hasError())
3259 return false;
3260
3261 // no need to block tail calls: the function body isn't visited here.
3262 RegisterScope scope(this);
3263
3264 if (_functionContext->contextType == ContextType::Binding)
3265 referenceForName(ast->name.toString(), true).loadInAccumulator();
3266 exprAccept(nx);
3267 return false;
3268}
3269
3270bool Codegen::visit(YieldExpression *ast)
3271{
3272 if (inFormalParameterList) {
3273 throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists"));
3274 return false;
3275 }
3276
3277 auto innerMostCurentFunctionContext = _context;
3278 while (innerMostCurentFunctionContext && innerMostCurentFunctionContext->contextType != ContextType::Function)
3279 innerMostCurentFunctionContext = innerMostCurentFunctionContext->parent;
3280
3281 Q_ASSERT(innerMostCurentFunctionContext); // yield outside function would have been rejected by parser
3282
3283 if (!innerMostCurentFunctionContext->isGenerator) {
3284 throwSyntaxError(ast->firstSourceLocation(), u"Yield is only valid in generator functions"_s);
3285 return false;
3286 }
3287
3288 RegisterScope scope(this);
3289 TailCallBlocker blockTailCalls(this);
3290 Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
3291 if (hasError())
3292 return false;
3293
3294 Reference acc = Reference::fromAccumulator(this);
3295
3296 if (ast->isYieldStar) {
3297 Reference iterator = Reference::fromStackSlot(this);
3298 Reference lhsValue = Reference::fromConst(this, Encode::undefined()).storeOnStack();
3299
3300 expr.loadInAccumulator();
3301 Instruction::GetIterator getIterator;
3302 getIterator.iterator = static_cast<int>(AST::ForEachType::Of);
3303 bytecodeGenerator->addInstruction(getIterator);
3304 iterator.storeConsumeAccumulator();
3305 Instruction::LoadUndefined load;
3306 bytecodeGenerator->addInstruction(load);
3307
3308 BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
3309 bytecodeGenerator->jump().link(in);
3310
3311 BytecodeGenerator::Label loop = bytecodeGenerator->label();
3312
3313 lhsValue.loadInAccumulator();
3314 Instruction::YieldStar yield;
3315 bytecodeGenerator->addInstruction(yield);
3316
3317 in.link();
3318
3319 Instruction::IteratorNextForYieldStar next;
3320 next.object = lhsValue.stackSlot();
3321 next.iterator = iterator.stackSlot();
3322 BytecodeGenerator::Jump done = bytecodeGenerator->addJumpInstruction(next);
3323 bytecodeGenerator->jumpNotUndefined().link(loop);
3324
3325 lhsValue.loadInAccumulator();
3326 emitReturn(acc);
3327
3328
3329 done.link();
3330 bytecodeGenerator->checkException();
3331
3332 lhsValue.loadInAccumulator();
3333 setExprResult(acc);
3334 return false;
3335 }
3336
3337 expr.loadInAccumulator();
3338 Instruction::Yield yield;
3339 bytecodeGenerator->addInstruction(yield);
3340 Instruction::Resume resume;
3341 BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
3342 emitReturn(acc);
3343 jump.link();
3344 setExprResult(acc);
3345 return false;
3346}
3347
3348static bool endsWithReturn(Module *module, Node *node)
3349{
3350 if (!node)
3351 return false;
3352 if (AST::cast<ReturnStatement *>(node))
3353 return true;
3354 if (AST::cast<ThrowStatement *>(node))
3355 return true;
3356 if (Program *p = AST::cast<Program *>(node))
3357 return endsWithReturn(module, p->statements);
3358 if (StatementList *sl = AST::cast<StatementList *>(node)) {
3359 while (sl->next)
3360 sl = sl->next;
3361 return endsWithReturn(module, sl->statement);
3362 }
3363 if (Block *b = AST::cast<Block *>(node)) {
3364 Context *blockContext = module->contextMap.value(node);
3365 if (blockContext->requiresExecutionContext)
3366 // we need to emit a return statement here, because of the
3367 // unwind handler
3368 return false;
3369 return endsWithReturn(module, b->statements);
3370 }
3371 if (IfStatement *is = AST::cast<IfStatement *>(node))
3372 return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
3373 return false;
3374}
3375
3376int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals,
3377 AST::StatementList *body)
3378{
3379 enterContext(ast);
3380
3381 if (_context->functionIndex >= 0)
3382 // already defined
3383 return leaveContext();
3384
3385 _context->name = name.isEmpty() ? currentExpr().result().name : name;
3386 _module->functions.append(_context);
3387 _context->functionIndex = _module->functions.size() - 1;
3388
3389 Context *savedFunctionContext = _functionContext;
3390 _functionContext = _context;
3391 ControlFlow *savedControlFlow = controlFlow;
3392 controlFlow = nullptr;
3393
3394 if (_context->contextType == ContextType::Global || _context->contextType == ContextType::ScriptImportedByQML) {
3395 _module->blocks.append(_context);
3396 _context->blockIndex = _module->blocks.size() - 1;
3397 }
3398 if (_module->debugMode) // allow the debugger to see overwritten arguments
3399 _context->argumentsCanEscape = true;
3400
3401 // When a user writes the following QML signal binding:
3402 // onSignal: function() { doSomethingUsefull }
3403 // we will generate a binding function that just returns the closure. However, that's not useful
3404 // at all, because if the onSignal is a signal handler, the user is actually making it explicit
3405 // that the binding is a function, so we should execute that. However, we don't know that during
3406 // AOT compilation, so mark the surrounding function as only-returning-a-closure.
3407 _context->returnsClosure = body && cast<ExpressionStatement *>(body->statement)
3408 && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression);
3409
3410 BytecodeGenerator bytecode(_context->line, _module->debugMode, storeSourceLocations);
3411 BytecodeGenerator *savedBytecodeGenerator;
3412 savedBytecodeGenerator = bytecodeGenerator;
3413 bytecodeGenerator = &bytecode;
3414 bytecodeGenerator->setLocation(ast->firstSourceLocation());
3415 BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
3416 _returnLabel = nullptr;
3417
3418 bool savedFunctionEndsWithReturn = functionEndsWithReturn;
3419 functionEndsWithReturn = endsWithReturn(_module, body);
3420
3421 // reserve the js stack frame (Context & js Function & accumulator)
3422 bytecodeGenerator->newRegisterArray(
3423 sizeof(CallData) / sizeof(StaticValue) - 1 + _context->arguments.size());
3424
3425 bool _inFormalParameterList = false;
3426 qSwap(_inFormalParameterList, inFormalParameterList);
3427
3428 int returnAddress = -1;
3429 bool _requiresReturnValue = _context->requiresImplicitReturnValue();
3430 qSwap(requiresReturnValue, _requiresReturnValue);
3431 returnAddress = bytecodeGenerator->newRegister();
3432 qSwap(_returnAddress, returnAddress);
3433
3434 // register the lexical scope for global code
3435 if (!_context->parent && _context->requiresExecutionContext) {
3436 _module->blocks.append(_context);
3437 _context->blockIndex = _module->blocks.size() - 1;
3438 }
3439
3440 TailCallBlocker maybeBlockTailCalls(this, _context->canHaveTailCalls());
3441
3442 RegisterScope registerScope(this);
3443 _context->emitBlockHeader(this);
3444
3445 {
3446 QScopedValueRollback<bool> inFormals(inFormalParameterList, true);
3447 TailCallBlocker blockTailCalls(this); // we're not in the FunctionBody or ConciseBody yet
3448
3449 int argc = 0;
3450 while (formals) {
3451 PatternElement *e = formals->element;
3452 if (!e) {
3453 if (!formals->next)
3454 // trailing comma
3455 break;
3456 Q_UNREACHABLE();
3457 }
3458
3459 Reference arg = referenceForName(e->bindingIdentifier.toString(), true);
3460 if (e->type == PatternElement::RestElement) {
3461 Q_ASSERT(!formals->next);
3462 Instruction::CreateRestParameter rest;
3463 rest.argIndex = argc;
3464 bytecodeGenerator->addInstruction(rest);
3465 arg.storeConsumeAccumulator();
3466 } else {
3467 if (e->bindingTarget || e->initializer) {
3468 initializeAndDestructureBindingElement(e, arg);
3469 if (hasError())
3470 break;
3471 }
3472 }
3473 formals = formals->next;
3474 ++argc;
3475 }
3476 }
3477
3478 if (_context->isGenerator) {
3479 Instruction::Yield yield;
3480 bytecodeGenerator->addInstruction(yield);
3481 }
3482
3483 statementList(body);
3484
3485 if (!hasError()) {
3486 bytecodeGenerator->setLocation(ast->lastSourceLocation());
3487 _context->emitBlockFooter(this);
3488
3489 if (_returnLabel || !functionEndsWithReturn) {
3490 if (_returnLabel)
3491 _returnLabel->link();
3492
3493 if (_returnLabel || requiresReturnValue) {
3494 Instruction::LoadReg load;
3495 load.reg = Moth::StackSlot::createRegister(_returnAddress);
3496 bytecodeGenerator->addInstruction(load);
3497 } else {
3498 Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
3499 }
3500
3501 bytecodeGenerator->addInstruction(Instruction::Ret());
3502 }
3503
3504 Q_ASSERT(_context == _functionContext);
3505 bytecodeGenerator->finalize(_context);
3506 _context->registerCountInFunction = bytecodeGenerator->registerCount();
3507 static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
3508 if (showCode) {
3509 qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
3510 << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
3511 qDebug().noquote() << QV4::Moth::dumpBytecode(
3512 _context->code, _context->locals.size(), _context->arguments.size(),
3513 _context->line, _context->lineAndStatementNumberMapping);
3514 qDebug();
3515 }
3516 }
3517
3518 qSwap(_returnAddress, returnAddress);
3519 qSwap(requiresReturnValue, _requiresReturnValue);
3520 qSwap(_inFormalParameterList, inFormalParameterList);
3521 bytecodeGenerator = savedBytecodeGenerator;
3522 delete _returnLabel;
3523 _returnLabel = savedReturnLabel;
3524 controlFlow = savedControlFlow;
3525 functionEndsWithReturn = savedFunctionEndsWithReturn;
3526 _functionContext = savedFunctionContext;
3527
3528 return leaveContext();
3529}
3530
3531bool Codegen::visit(Block *ast)
3532{
3533 if (hasError())
3534 return false;
3535
3536 RegisterScope scope(this);
3537
3538 ControlFlowBlock controlFlow(this, ast);
3539 statementList(ast->statements);
3540 return false;
3541}
3542
3543bool Codegen::visit(BreakStatement *ast)
3544{
3545 if (hasError())
3546 return false;
3547
3548 // no need to block tail calls here: children aren't visited
3549 if (!controlFlow) {
3550 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
3551 return false;
3552 }
3553
3554 ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
3555 if (!target.linkLabel.isValid()) {
3556 if (ast->label.isEmpty())
3557 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
3558 else
3559 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
3560 return false;
3561 }
3562
3563 bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
3564
3565 return false;
3566}
3567
3568bool Codegen::visit(ContinueStatement *ast)
3569{
3570 if (hasError())
3571 return false;
3572
3573 // no need to block tail calls here: children aren't visited
3574 RegisterScope scope(this);
3575
3576 if (!controlFlow) {
3577 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
3578 return false;
3579 }
3580
3581 ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
3582 if (!target.linkLabel.isValid()) {
3583 if (ast->label.isEmpty())
3584 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
3585 else
3586 throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop"));
3587 return false;
3588 }
3589
3590 bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
3591
3592 return false;
3593}
3594
3595bool Codegen::visit(DebuggerStatement *)
3596{
3597 Q_UNIMPLEMENTED();
3598 return false;
3599}
3600
3601bool Codegen::visit(DoWhileStatement *ast)
3602{
3603 if (hasError())
3604 return false;
3605
3606 RegisterScope scope(this);
3607
3608 BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
3609 BytecodeGenerator::Label cond = bytecodeGenerator->newLabel();
3610 BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
3611
3612 ControlFlowLoop flow(this, &end, &cond);
3613
3614 // special case that is not a loop:
3615 // do {...} while (false)
3616 if (!AST::cast<FalseLiteral *>(ast->expression))
3617 bytecodeGenerator->addLoopStart(body);
3618
3619 body.link();
3620 statement(ast->statement);
3621 setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken);
3622
3623 cond.link();
3624 if (AST::cast<TrueLiteral *>(ast->expression)) {
3625 // do {} while (true) -> just jump back to the loop body, no need to generate a condition
3626 bytecodeGenerator->checkException();
3627 bytecodeGenerator->jump().link(body);
3628 } else if (AST::cast<FalseLiteral *>(ast->expression)) {
3629 // do {} while (false) -> fall through, no need to generate a condition
3630 } else {
3631 TailCallBlocker blockTailCalls(this);
3632 bytecodeGenerator->checkException();
3633 condition(ast->expression, &body, &end, false);
3634 }
3635
3636 end.link();
3637
3638 return false;
3639}
3640
3641bool Codegen::visit(EmptyStatement *)
3642{
3643 return false;
3644}
3645
3646bool Codegen::visit(ExpressionStatement *ast)
3647{
3648 if (hasError())
3649 return false;
3650
3651 RegisterScope scope(this);
3652 TailCallBlocker blockTailCalls(this);
3653
3654 if (requiresReturnValue) {
3655 Reference e = expression(ast->expression);
3656 if (hasError())
3657 return false;
3658 (void) e.storeOnStack(_returnAddress);
3659 } else {
3660 statement(ast->expression);
3661 }
3662 return false;
3663}
3664
3665bool Codegen::visit(ForEachStatement *ast)
3666{
3667 if (hasError())
3668 return false;
3669
3670 RegisterScope scope(this);
3671 TailCallBlocker blockTailCalls(this);
3672
3673 Reference iterator = Reference::fromStackSlot(this);
3674 Reference lhsValue = Reference::fromStackSlot(this);
3675
3676 // There should be a temporal block, so that variables declared in lhs shadow outside vars.
3677 // This block should define a temporal dead zone for those variables.
3678 {
3679 RegisterScope innerScope(this);
3680 ControlFlowBlock controlFlow(this, ast);
3681 Reference expr = expression(ast->expression);
3682 if (hasError())
3683 return false;
3684
3685 expr.loadInAccumulator();
3686 Instruction::GetIterator iteratorObjInstr;
3687 iteratorObjInstr.iterator = static_cast<int>(ast->type);
3688 bytecodeGenerator->addInstruction(iteratorObjInstr);
3689 iterator.storeConsumeAccumulator();
3690 }
3691
3692 BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
3693 BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
3694 BytecodeGenerator::Label done;
3695
3696 {
3697 std::function<void()> cleanup;
3698 if (ast->type == ForEachType::Of) {
3699 done = bytecodeGenerator->newLabel();
3700 cleanup = [iterator, this, done]() {
3701 iterator.loadInAccumulator();
3702 Instruction::IteratorClose close;
3703 bytecodeGenerator->addInstruction(close);
3704 done.link();
3705 };
3706 } else {
3707 done = end;
3708 }
3709 ControlFlowLoop flow(this, &end, &in, std::move(cleanup));
3710 bytecodeGenerator->addLoopStart(in);
3711 in.link();
3712 iterator.loadInAccumulator();
3713 Instruction::IteratorNext next;
3714 next.value = lhsValue.stackSlot();
3715 bytecodeGenerator->addJumpInstruction(next).link(done);
3716
3717 // each iteration gets it's own context, as per spec
3718 {
3719 RegisterScope innerScope(this);
3720 ControlFlowBlock controlFlow(this, ast);
3721
3722 if (ExpressionNode *e = ast->lhs->expressionCast()) {
3723 if (AST::Pattern *p = e->patternCast()) {
3724 RegisterScope scope(this);
3725 destructurePattern(p, lhsValue);
3726 } else {
3727 Reference lhs = expression(e);
3728 if (hasError())
3729 goto error;
3730 if (!lhs.isLValue()) {
3731 throwReferenceError(e->firstSourceLocation(), QStringLiteral("Invalid left-hand side expression for 'in' expression"));
3732 goto error;
3733 }
3734 lhs = lhs.asLValue();
3735 lhsValue.loadInAccumulator();
3736 lhs.storeConsumeAccumulator();
3737 }
3738 } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
3739 initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
3740 if (hasError())
3741 goto error;
3742 } else {
3743 Q_UNREACHABLE();
3744 }
3745
3746 blockTailCalls.unblock();
3747 statement(ast->statement);
3748 setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
3749 }
3750
3751 bytecodeGenerator->checkException();
3752 bytecodeGenerator->jump().link(in);
3753
3754 error:
3755 end.link();
3756
3757 // all execution paths need to end up here (normal loop exit, break, and exceptions) in
3758 // order to reset the unwind handler, and to close the iterator in calse of an for-of loop.
3759 }
3760
3761 return false;
3762}
3763
3764bool Codegen::visit(ForStatement *ast)
3765{
3766 if (hasError())
3767 return false;
3768
3769 RegisterScope scope(this);
3770 TailCallBlocker blockTailCalls(this);
3771
3772 ControlFlowBlock controlFlow(this, ast);
3773
3774 if (ast->initialiser)
3775 statement(ast->initialiser);
3776 else if (ast->declarations)
3777 variableDeclarationList(ast->declarations);
3778
3779 BytecodeGenerator::Label cond = bytecodeGenerator->label();
3780 BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
3781 BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
3782 BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
3783
3784 ControlFlowLoop flow(this, &end, &step);
3785 bytecodeGenerator->addLoopStart(cond);
3786 condition(ast->condition, &body, &end, true);
3787
3788 body.link();
3789 blockTailCalls.unblock();
3790 statement(ast->statement);
3791 blockTailCalls.reblock();
3792 setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
3793
3794 step.link();
3795 if (_context->requiresExecutionContext) {
3796 Instruction::CloneBlockContext clone;
3797 bytecodeGenerator->addInstruction(clone);
3798 }
3799 statement(ast->expression);
3800 bytecodeGenerator->checkException();
3801 bytecodeGenerator->jump().link(cond);
3802
3803 end.link();
3804
3805 return false;
3806}
3807
3808bool Codegen::visit(IfStatement *ast)
3809{
3810 if (hasError())
3811 return false;
3812
3813 RegisterScope scope(this);
3814 TailCallBlocker blockTailCalls(this);
3815
3816 BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel();
3817 BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel();
3818 condition(ast->expression, &trueLabel, &falseLabel, true);
3819 blockTailCalls.unblock();
3820
3821 trueLabel.link();
3822 statement(ast->ok);
3823 if (ast->ko) {
3824 if (endsWithReturn(_module, ast)) {
3825 falseLabel.link();
3826 statement(ast->ko);
3827 } else {
3828 BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump();
3829 falseLabel.link();
3830 statement(ast->ko);
3831 jump_endif.link();
3832 }
3833 } else {
3834 falseLabel.link();
3835 }
3836
3837 return false;
3838}
3839
3840bool Codegen::visit(LabelledStatement *ast)
3841{
3842 if (hasError())
3843 return false;
3844
3845 RegisterScope scope(this);
3846
3847 // check that no outer loop contains the label
3848 ControlFlow *l = controlFlow;
3849 while (l) {
3850 if (l->label() == ast->label) {
3851 QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
3852 throwSyntaxError(ast->firstSourceLocation(), error);
3853 return false;
3854 }
3855 l = l->parent;
3856 }
3857 _labelledStatement = ast;
3858
3859 if (AST::cast<AST::SwitchStatement *>(ast->statement) ||
3860 AST::cast<AST::WhileStatement *>(ast->statement) ||
3861 AST::cast<AST::DoWhileStatement *>(ast->statement) ||
3862 AST::cast<AST::ForStatement *>(ast->statement) ||
3863 AST::cast<AST::ForEachStatement *>(ast->statement)) {
3864 statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
3865 } else {
3866 BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
3867 ControlFlowLoop flow(this, &breakLabel);
3868 statement(ast->statement);
3869 breakLabel.link();
3870 }
3871
3872 return false;
3873}
3874
3875void Codegen::emitReturn(const Reference &expr)
3876{
3877 ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
3878 if (target.linkLabel.isValid() && target.unwindLevel) {
3879 Q_ASSERT(_returnAddress >= 0);
3880 (void) expr.storeOnStack(_returnAddress);
3881 bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
3882 } else {
3883 expr.loadInAccumulator();
3884 bytecodeGenerator->addInstruction(Instruction::Ret());
3885 }
3886}
3887
3888bool Codegen::visit(ReturnStatement *ast)
3889{
3890 if (hasError())
3891 return false;
3892
3893 if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
3894 throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
3895 return false;
3896 }
3897 Reference expr;
3898 if (ast->expression) {
3899 expr = expression(ast->expression);
3900 if (hasError())
3901 return false;
3902 } else {
3903 expr = Reference::fromConst(this, Encode::undefined());
3904 }
3905
3906 emitReturn(expr);
3907
3908 return false;
3909}
3910
3911bool Codegen::visit(SwitchStatement *ast)
3912{
3913 if (hasError())
3914 return false;
3915
3916 if (requiresReturnValue)
3917 Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
3918
3919 RegisterScope scope(this);
3920 TailCallBlocker blockTailCalls(this);
3921
3922 if (ast->block) {
3923 BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
3924
3925 Reference lhs = expression(ast->expression);
3926 if (hasError())
3927 return false;
3928 lhs = lhs.storeOnStack();
3929
3930 ControlFlowBlock controlFlow(this, ast->block);
3931
3932 // set up labels for all clauses
3933 QHash<Node *, BytecodeGenerator::Label> blockMap;
3934 for (CaseClauses *it = ast->block->clauses; it; it = it->next)
3935 blockMap[it->clause] = bytecodeGenerator->newLabel();
3936 if (ast->block->defaultClause)
3937 blockMap[ast->block->defaultClause] = bytecodeGenerator->newLabel();
3938 for (CaseClauses *it = ast->block->moreClauses; it; it = it->next)
3939 blockMap[it->clause] = bytecodeGenerator->newLabel();
3940
3941 // do the switch conditions
3942 for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
3943 CaseClause *clause = it->clause;
3944 Reference rhs = expression(clause->expression);
3945 if (hasError())
3946 return false;
3947 rhs.loadInAccumulator();
3948 bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
3949 }
3950
3951 for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
3952 CaseClause *clause = it->clause;
3953 Reference rhs = expression(clause->expression);
3954 if (hasError())
3955 return false;
3956 rhs.loadInAccumulator();
3957 bytecodeGenerator->jumpStrictEqual(lhs.stackSlot(), blockMap.value(clause));
3958 }
3959
3960 if (DefaultClause *defaultClause = ast->block->defaultClause)
3961 bytecodeGenerator->jump().link(blockMap.value(defaultClause));
3962 else
3963 bytecodeGenerator->jump().link(switchEnd);
3964
3965 ControlFlowLoop flow(this, &switchEnd);
3966
3967 insideSwitch = true;
3968 blockTailCalls.unblock();
3969 for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
3970 CaseClause *clause = it->clause;
3971 blockMap[clause].link();
3972
3973 statementList(clause->statements);
3974 }
3975
3976 if (ast->block->defaultClause) {
3977 DefaultClause *clause = ast->block->defaultClause;
3978 blockMap[clause].link();
3979
3980 statementList(clause->statements);
3981 }
3982
3983 for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) {
3984 CaseClause *clause = it->clause;
3985 blockMap[clause].link();
3986
3987 statementList(clause->statements);
3988 }
3989 insideSwitch = false;
3990
3991 switchEnd.link();
3992
3993 }
3994
3995 return false;
3996}
3997
3998bool Codegen::visit(ThrowStatement *ast)
3999{
4000 if (hasError())
4001 return false;
4002
4003 RegisterScope scope(this);
4004 TailCallBlocker blockTailCalls(this);
4005
4006 Reference expr = expression(ast->expression);
4007 if (hasError())
4008 return false;
4009
4010 expr.loadInAccumulator();
4011 Instruction::ThrowException instr;
4012 bytecodeGenerator->addInstruction(instr);
4013 return false;
4014}
4015
4016void Codegen::handleTryCatch(TryStatement *ast)
4017{
4018 Q_ASSERT(ast);
4019 RegisterScope scope(this);
4020 {
4021 ControlFlowCatch catchFlow(this, ast->catchExpression);
4022 RegisterScope scope(this);
4023 TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before catch is generated
4024 statement(ast->statement);
4025 }
4026}
4027
4028void Codegen::handleTryFinally(TryStatement *ast)
4029{
4030 RegisterScope scope(this);
4031 const bool hasCatchBlock = ast->catchExpression;
4032 ControlFlowFinally finally(this, ast->finallyExpression, hasCatchBlock);
4033 TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated
4034
4035 if (ast->catchExpression) {
4036 handleTryCatch(ast);
4037 } else {
4038 RegisterScope scope(this);
4039 statement(ast->statement);
4040 }
4041}
4042
4043bool Codegen::visit(TryStatement *ast)
4044{
4045 if (hasError())
4046 return false;
4047
4048 RegisterScope scope(this);
4049
4050 if (ast->finallyExpression && ast->finallyExpression->statement) {
4051 handleTryFinally(ast);
4052 } else {
4053 handleTryCatch(ast);
4054 }
4055
4056 return false;
4057}
4058
4059bool Codegen::visit(VariableStatement *ast)
4060{
4061 if (hasError())
4062 return false;
4063
4064 variableDeclarationList(ast->declarations);
4065 return false;
4066}
4067
4068bool Codegen::visit(WhileStatement *ast)
4069{
4070 if (hasError())
4071 return false;
4072
4073 if (AST::cast<FalseLiteral *>(ast->expression))
4074 return false;
4075
4076 RegisterScope scope(this);
4077
4078 BytecodeGenerator::Label start = bytecodeGenerator->newLabel();
4079 BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
4080 BytecodeGenerator::Label cond = bytecodeGenerator->label();
4081 ControlFlowLoop flow(this, &end, &cond);
4082 bytecodeGenerator->addLoopStart(cond);
4083
4084 bytecodeGenerator->checkException();
4085
4086 if (!AST::cast<TrueLiteral *>(ast->expression)) {
4087 TailCallBlocker blockTailCalls(this);
4088 condition(ast->expression, &start, &end, true);
4089 }
4090
4091 start.link();
4092 statement(ast->statement);
4093 setJumpOutLocation(bytecodeGenerator, ast->statement, ast->whileToken);
4094 bytecodeGenerator->jump().link(cond);
4095
4096 end.link();
4097 return false;
4098}
4099
4100bool Codegen::visit(WithStatement *ast)
4101{
4102 if (hasError())
4103 return false;
4104
4105 RegisterScope scope(this);
4106 TailCallBlocker blockTailCalls(this);
4107
4108 Reference src = expression(ast->expression);
4109 if (hasError())
4110 return false;
4111 src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
4112 src.loadInAccumulator();
4113
4114 enterContext(ast);
4115 {
4116 blockTailCalls.unblock();
4117 ControlFlowWith flow(this);
4118 statement(ast->statement);
4119 }
4120 leaveContext();
4121
4122 return false;
4123}
4124
4125bool Codegen::visit(UiArrayBinding *)
4126{
4127 Q_UNIMPLEMENTED();
4128 return false;
4129}
4130
4131bool Codegen::visit(UiObjectBinding *)
4132{
4133 Q_UNIMPLEMENTED();
4134 return false;
4135}
4136
4137bool Codegen::visit(UiObjectDefinition *)
4138{
4139 Q_UNIMPLEMENTED();
4140 return false;
4141}
4142
4143bool Codegen::visit(UiPublicMember *)
4144{
4145 Q_UNIMPLEMENTED();
4146 return false;
4147}
4148
4149bool Codegen::visit(UiScriptBinding *)
4150{
4151 Q_UNIMPLEMENTED();
4152 return false;
4153}
4154
4155bool Codegen::visit(UiSourceElement *)
4156{
4157 Q_UNIMPLEMENTED();
4158 return false;
4159}
4160
4161bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const SourceLocation& loc)
4162{
4163 if (!_context->isStrict)
4164 return false;
4165 bool isArgOrEval = false;
4166 if (r.type == Reference::Name) {
4167 QString str = jsUnitGenerator->stringForIndex(r.nameAsIndex());
4168 if (str == QLatin1String("eval") || str == QLatin1String("arguments")) {
4169 isArgOrEval = true;
4170 }
4171 } else if (r.type == Reference::ScopedLocal || r.isRegister()) {
4172 isArgOrEval = r.isArgOrEval;
4173 }
4174 if (isArgOrEval)
4175 throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
4176 return isArgOrEval;
4177}
4178
4179void Codegen::throwError(ErrorType errorType, const SourceLocation &loc, const QString &detail)
4180{
4181 if (hasError())
4182 return;
4183
4184 _errorType = errorType;
4185 _error.message = detail;
4186 _error.loc = loc;
4187}
4188
4189void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail)
4190{
4191 throwError(SyntaxError, loc, detail);
4192}
4193
4194void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail)
4195{
4196 throwError(ReferenceError, loc, detail);
4197}
4198
4199QQmlJS::DiagnosticMessage Codegen::error() const
4200{
4201 return _error;
4202}
4203
4204QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::generateCompilationUnit(
4205 bool generateUnitData)
4206{
4207 return QQmlRefPointer<QV4::CompiledData::CompilationUnit>(
4208 new QV4::CompiledData::CompilationUnit(
4209 generateUnitData ? jsUnitGenerator->generateUnit() : nullptr),
4210 QQmlRefPointer<QV4::CompiledData::CompilationUnit>::Adopt);
4211}
4212
4213QQmlRefPointer<QV4::CompiledData::CompilationUnit> Codegen::compileModule(
4214 bool debugMode, const QString &url, const QString &sourceCode,
4215 const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
4216{
4217 QQmlJS::Engine ee;
4218 QQmlJS::Lexer lexer(&ee);
4219 lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
4220 QQmlJS::Parser parser(&ee);
4221
4222 const bool parsed = parser.parseModule();
4223
4224 if (diagnostics)
4225 *diagnostics = parser.diagnosticMessages();
4226
4227 if (!parsed)
4228 return QQmlRefPointer<CompiledData::CompilationUnit>();
4229
4230 QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode());
4231 if (!moduleNode) {
4232 // if parsing was successful, and we have no module, then
4233 // the file was empty.
4234 if (diagnostics)
4235 diagnostics->clear();
4236 return nullptr;
4237 }
4238
4239 using namespace QV4::Compiler;
4240 Compiler::Module compilerModule(url, url, debugMode);
4241 compilerModule.unitFlags |= CompiledData::Unit::IsESModule;
4242 compilerModule.sourceTimeStamp = sourceTimeStamp;
4243 JSUnitGenerator jsGenerator(&compilerModule);
4244 Codegen cg(&jsGenerator, /*strictMode*/true);
4245 cg.generateFromModule(sourceCode, moduleNode, &compilerModule);
4246 if (cg.hasError()) {
4247 if (diagnostics)
4248 *diagnostics << cg.error();
4249 return QQmlRefPointer<CompiledData::CompilationUnit>();
4250 }
4251
4252 return cg.generateCompilationUnit();
4253}
4254
4255const QV4::CompiledData::Unit *Codegen::generateNativeModuleUnitData(
4256 bool debugMode, const QString &url, const Value &value)
4257{
4258 using namespace QV4::Compiler;
4259 Compiler::Module compilerModule(url, url, debugMode);
4260 compilerModule.unitFlags |= CompiledData::Unit::IsESModule;
4261 JSUnitGenerator jsGenerator(&compilerModule);
4262 Codegen cg(&jsGenerator, /*strictMode*/true);
4263 cg.generateFromModule(value, &compilerModule);
4264 Q_ASSERT(!cg.hasError());
4265 return jsGenerator.generateUnit();
4266}
4267
4269{
4270 VolatileMemoryLocations locs;
4271 Codegen *parent;
4272
4273public:
4278
4280 {
4281 s->accept(this);
4282 return locs;
4283 }
4284
4285 bool visit(ArrayMemberExpression *) override
4286 {
4287 locs.setAllVolatile();
4288 return false;
4289 }
4290
4291 bool visit(FieldMemberExpression *) override
4292 {
4293 locs.setAllVolatile();
4294 return false;
4295 }
4296
4297 bool visit(PostIncrementExpression *e) override
4298 {
4299 collectIdentifiers(locs.specificLocations, e->base);
4300 return false;
4301 }
4302
4303 bool visit(PostDecrementExpression *e) override
4304 {
4305 collectIdentifiers(locs.specificLocations, e->base);
4306 return false;
4307 }
4308
4309 bool visit(PreIncrementExpression *e) override
4310 {
4311 collectIdentifiers(locs.specificLocations, e->expression);
4312 return false;
4313 }
4314
4315 bool visit(PreDecrementExpression *e) override
4316 {
4317 collectIdentifiers(locs.specificLocations, e->expression);
4318 return false;
4319 }
4320
4321 bool visit(BinaryExpression *e) override
4322 {
4323 switch (e->op) {
4324 case QSOperator::InplaceAnd:
4325 case QSOperator::InplaceSub:
4326 case QSOperator::InplaceDiv:
4327 case QSOperator::InplaceAdd:
4328 case QSOperator::InplaceLeftShift:
4329 case QSOperator::InplaceMod:
4330 case QSOperator::InplaceMul:
4331 case QSOperator::InplaceOr:
4332 case QSOperator::InplaceRightShift:
4333 case QSOperator::InplaceURightShift:
4334 case QSOperator::InplaceXor:
4335 collectIdentifiers(locs.specificLocations, e);
4336 return false;
4337
4338 default:
4339 return true;
4340 }
4341 }
4342
4347
4348private:
4349 void collectIdentifiers(QList<QStringView> &ids, AST::Node *node) {
4350 class Collector: public QQmlJS::AST::Visitor {
4351 private:
4352 QList<QStringView> &ids;
4353 VolatileMemoryLocationScanner *parent;
4354
4355 public:
4356 Collector(QList<QStringView> &ids, VolatileMemoryLocationScanner *parent) :
4357 QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent)
4358 {}
4359
4360 bool visit(IdentifierExpression *ie) final {
4361 ids.append(ie->name);
4362 return false;
4363 }
4364
4365 void throwRecursionDepthError() final
4366 {
4367 parent->throwRecursionDepthError();
4368 }
4369 };
4370 Collector collector(ids, this);
4371 node->accept(&collector);
4372 }
4373};
4374
4375Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast)
4376{
4377 VolatileMemoryLocationScanner scanner(this);
4378 return scanner.scan(ast);
4379}
4380
4381QUrl Codegen::url() const
4382{
4383 return QUrl(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName));
4384}
4385
4386bool Codegen::RValue::operator==(const RValue &other) const
4387{
4388 switch (type) {
4389 case Accumulator:
4390 return other.isAccumulator();
4391 case StackSlot:
4392 return other.isStackSlot() && theStackSlot == other.theStackSlot;
4393 case Const:
4394 return other.isConst() && constant == other.constant;
4395 default:
4396 return false;
4397 }
4398}
4399
4400Codegen::RValue Codegen::RValue::storeOnStack() const
4401{
4402 switch (type) {
4403 case Accumulator:
4404 return RValue::fromStackSlot(codegen, Reference::fromAccumulator(codegen).storeOnStack().stackSlot());
4405 case StackSlot:
4406 return *this;
4407 case Const:
4408 return RValue::fromStackSlot(codegen, Reference::storeConstOnStack(codegen, constant).stackSlot());
4409 default:
4410 Q_UNREACHABLE();
4411 }
4412}
4413
4414void Codegen::RValue::loadInAccumulator() const
4415{
4416 switch (type) {
4417 case Accumulator:
4418 // nothing to do
4419 return;
4420 case StackSlot:
4421 return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator();
4422 case Const:
4423 return Reference::fromConst(codegen, constant).loadInAccumulator();
4424 default:
4425 Q_UNREACHABLE();
4426 }
4427
4428}
4429
4430bool Codegen::Reference::operator==(const Codegen::Reference &other) const
4431{
4432 if (type != other.type)
4433 return false;
4434 switch (type) {
4435 case Invalid:
4436 case Accumulator:
4437 break;
4438 case Super:
4439 return true;
4440 case SuperProperty:
4441 return property == other.property;
4442 case StackSlot:
4443 return theStackSlot == other.theStackSlot;
4444 case ScopedLocal:
4445 return index == other.index && scope == other.scope;
4446 case Name:
4447 return nameAsIndex() == other.nameAsIndex();
4448 case Member:
4449 return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex;
4450 case Subscript:
4451 return elementBase == other.elementBase && other.subscriptLoadedForCall
4452 ? (subscriptLoadedForCall && element == other.element)
4453 : (!subscriptLoadedForCall && elementSubscript == other.elementSubscript);
4454 case Import:
4455 return index == other.index;
4456 case Const:
4457 return constant == other.constant;
4458 }
4459 return true;
4460}
4461
4462Codegen::RValue Codegen::Reference::asRValue() const
4463{
4464 switch (type) {
4465 case Invalid:
4466 Q_UNREACHABLE();
4467 case Accumulator:
4468 return RValue::fromAccumulator(codegen);
4469 case StackSlot:
4470 return RValue::fromStackSlot(codegen, stackSlot());
4471 case Const:
4472 return RValue::fromConst(codegen, constant);
4473 default:
4474 loadInAccumulator();
4475 return RValue::fromAccumulator(codegen);
4476 }
4477}
4478
4479Codegen::Reference Codegen::Reference::asLValue() const
4480{
4481 switch (type) {
4482 case Invalid:
4483 case Accumulator:
4484 Q_UNREACHABLE();
4485 case Super:
4486 codegen->throwSyntaxError(SourceLocation(), QStringLiteral("Super lvalues not implemented."));
4487 return *this;
4488 case Member:
4489 if (!propertyBase.isStackSlot()) {
4490 Reference r = *this;
4491 r.propertyBase = propertyBase.storeOnStack();
4492 return r;
4493 }
4494 return *this;
4495 case Subscript:
4496 if (!elementSubscript.isStackSlot()) {
4497 Reference r = *this;
4498 r.elementSubscript = elementSubscript.storeOnStack();
4499 return r;
4500 }
4501 return *this;
4502 default:
4503 return *this;
4504 }
4505}
4506
4507Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
4508{
4509 storeAccumulator(); // it doesn't matter what happens here, just do it.
4510 return Reference();
4511}
4512
4513Codegen::Reference Codegen::Reference::baseObject() const
4514{
4515 if (type == Reference::Member) {
4516 RValue rval = propertyBase;
4517 if (!rval.isValid())
4518 return Reference::fromConst(codegen, Encode::undefined());
4519 if (rval.isAccumulator())
4520 return Reference::fromAccumulator(codegen);
4521 if (rval.isStackSlot())
4522 return Reference::fromStackSlot(codegen, rval.stackSlot());
4523 if (rval.isConst())
4524 return Reference::fromConst(codegen, rval.constantValue());
4525 Q_UNREACHABLE();
4526 } else if (type == Reference::Subscript) {
4527 return Reference::fromStackSlot(codegen, elementBase.stackSlot());
4528 } else if (type == Reference::SuperProperty) {
4529 return Reference::fromStackSlot(codegen, CallData::This);
4530 } else {
4531 return Reference::fromConst(codegen, Encode::undefined());
4532 }
4533}
4534
4535Codegen::Reference Codegen::Reference::storeOnStack() const
4536{ return doStoreOnStack(-1); }
4537
4538void Codegen::Reference::storeOnStack(int slotIndex) const
4539{ doStoreOnStack(slotIndex); }
4540
4541Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
4542{
4543 Q_ASSERT(isValid());
4544
4545 if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile) && !requiresTDZCheck)
4546 return *this;
4547
4548 if (isStackSlot() && !requiresTDZCheck) { // temp-to-temp move
4549 Reference dest = Reference::fromStackSlot(codegen, slotIndex);
4550 Instruction::MoveReg move;
4551 move.srcReg = stackSlot();
4552 move.destReg = dest.stackSlot();
4553 codegen->bytecodeGenerator->addInstruction(move);
4554 return dest;
4555 }
4556
4557 Reference slot = Reference::fromStackSlot(codegen, slotIndex);
4558 if (isConstant()) {
4559 Instruction::MoveConst move;
4560 move.constIndex = codegen->registerConstant(constant);
4561 move.destTemp = slot.stackSlot();
4562 codegen->bytecodeGenerator->addInstruction(move);
4563 } else {
4564 loadInAccumulator();
4565 slot.storeConsumeAccumulator();
4566 }
4567 return slot;
4568}
4569
4570void Codegen::Reference::tdzCheck(bool requiresCheck, bool throwsReferenceError) const {
4571 if (throwsReferenceError) {
4572 codegen->generateThrowException(QStringLiteral("ReferenceError"),
4573 name + QStringLiteral(" is not defined"));
4574 return;
4575 }
4576 if (!requiresCheck)
4577 return;
4578 Instruction::DeadTemporalZoneCheck check;
4579 check.name = codegen->registerString(name);
4580 codegen->bytecodeGenerator->addInstruction(check);
4581}
4582
4583void Codegen::Reference::tdzCheckStackSlot(Moth::StackSlot slot, bool requiresCheck, bool throwsReferenceError) const {
4584 if (!requiresCheck)
4585 return;
4586 Instruction::LoadReg load;
4587 load.reg = slot;
4588 codegen->bytecodeGenerator->addInstruction(load);
4589 tdzCheck(true, throwsReferenceError);
4590}
4591
4592Codegen::Reference Codegen::Reference::storeRetainAccumulator() const
4593{
4594 if (storeWipesAccumulator()) {
4595 // a store will
4596 auto tmp = Reference::fromStackSlot(codegen);
4597 tmp.storeAccumulator(); // this is safe, and won't destory the accumulator
4598 storeAccumulator();
4599 return tmp;
4600 } else {
4601 // ok, this is safe, just do the store.
4602 storeAccumulator();
4603 return *this;
4604 }
4605}
4606
4607bool Codegen::Reference::storeWipesAccumulator() const
4608{
4609 switch (type) {
4610 default:
4611 case Invalid:
4612 case Const:
4613 case Accumulator:
4614 Q_UNREACHABLE();
4615 return false;
4616 case StackSlot:
4617 case ScopedLocal:
4618 return false;
4619 case Name:
4620 case Member:
4621 case Subscript:
4622 return true;
4623 }
4624}
4625
4626void Codegen::Reference::storeAccumulator() const
4627{
4628 if (throwsReferenceError) {
4629 codegen->generateThrowException(QStringLiteral("ReferenceError"),
4630 name + QStringLiteral(" is not defined"));
4631 return;
4632 }
4633
4634 if (isReferenceToConst) {
4635 // throw a type error
4636 codegen->generateThrowException(QStringLiteral("TypeError"));
4637 return;
4638 }
4639
4640 switch (type) {
4641 case Super:
4642 Q_UNREACHABLE_RETURN();
4643 case SuperProperty:
4644 Instruction::StoreSuperProperty store;
4645 store.property = property.stackSlot();
4646 codegen->bytecodeGenerator->addInstruction(store);
4647 return;
4648 case StackSlot: {
4649 Instruction::StoreReg store;
4650 store.reg = theStackSlot;
4651 codegen->bytecodeGenerator->addInstruction(store);
4652 return;
4653 }
4654 case ScopedLocal: {
4655 if (scope == 0) {
4656 Instruction::StoreLocal store;
4657 store.index = index;
4658 codegen->bytecodeGenerator->addInstruction(store);
4659 } else {
4660 Instruction::StoreScopedLocal store;
4661 store.index = index;
4662 store.scope = scope;
4663 codegen->bytecodeGenerator->addInstruction(store);
4664 }
4665 return;
4666 }
4667 case Name: {
4668 Context *c = codegen->currentContext();
4669 if (c->isStrict) {
4670 Instruction::StoreNameStrict store;
4671 store.name = nameAsIndex();
4672 codegen->bytecodeGenerator->addInstruction(store);
4673 } else {
4674 Instruction::StoreNameSloppy store;
4675 store.name = nameAsIndex();
4676 codegen->bytecodeGenerator->addInstruction(store);
4677 }
4678 } return;
4679 case Member:
4680 if (codegen->useFastLookups) {
4681 Instruction::SetLookup store;
4682 store.base = propertyBase.stackSlot();
4683 store.index = codegen->registerSetterLookup(propertyNameIndex);
4684 codegen->bytecodeGenerator->addInstruction(store);
4685 } else {
4686 Instruction::StoreProperty store;
4687 store.base = propertyBase.stackSlot();
4688 store.name = propertyNameIndex;
4689 codegen->bytecodeGenerator->addInstruction(store);
4690 }
4691 return;
4692 case Subscript: {
4693 Instruction::StoreElement store;
4694 store.base = elementBase;
4695 store.index = elementSubscript.stackSlot();
4696 codegen->bytecodeGenerator->addInstruction(store);
4697 } return;
4698 case Invalid:
4699 case Accumulator:
4700 case Const:
4701 case Import:
4702 break;
4703 }
4704
4705 Q_UNREACHABLE();
4706}
4707
4708void Codegen::Reference::loadInAccumulator() const
4709{
4710 switch (type) {
4711 case Accumulator:
4712 return;
4713 case Super:
4714 Q_UNREACHABLE_RETURN();
4715 case SuperProperty:
4716 tdzCheckStackSlot(property, subscriptRequiresTDZCheck, false);
4717 Instruction::LoadSuperProperty load;
4718 load.property = property.stackSlot();
4719 codegen->bytecodeGenerator->addInstruction(load);
4720 return;
4721 case Const: {
4722QT_WARNING_PUSH
4723QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
4724 if (constant == Encode::null()) {
4725 Instruction::LoadNull load;
4726 codegen->bytecodeGenerator->addInstruction(load);
4727 } else if (constant == Encode(true)) {
4728 Instruction::LoadTrue load;
4729 codegen->bytecodeGenerator->addInstruction(load);
4730 } else if (constant == Encode(false)) {
4731 Instruction::LoadFalse load;
4732 codegen->bytecodeGenerator->addInstruction(load);
4733 } else if (constant == Encode::undefined()) {
4734 Instruction::LoadUndefined load;
4735 codegen->bytecodeGenerator->addInstruction(load);
4736 } else {
4737 StaticValue p = StaticValue::fromReturnedValue(constant);
4738 if (p.isNumber()) {
4739 double d = p.asDouble();
4740 int i = QJSNumberCoercion::toInteger(d);
4741 if (d == i && (d != 0 || !std::signbit(d))) {
4742 if (!i) {
4743 Instruction::LoadZero load;
4744 codegen->bytecodeGenerator->addInstruction(load);
4745 return;
4746 }
4747 Instruction::LoadInt load;
4748 load.value = StaticValue::fromReturnedValue(constant).toInt32();
4749 codegen->bytecodeGenerator->addInstruction(load);
4750 return;
4751 }
4752 }
4753 Instruction::LoadConst load;
4754 load.index = codegen->registerConstant(constant);
4755 codegen->bytecodeGenerator->addInstruction(load);
4756 }
4757QT_WARNING_POP
4758 } return;
4759 case StackSlot: {
4760 Instruction::LoadReg load;
4761 load.reg = stackSlot();
4762 codegen->bytecodeGenerator->addInstruction(load);
4763 tdzCheck(requiresTDZCheck, throwsReferenceError);
4764 } return;
4765 case ScopedLocal: {
4766 if (!scope) {
4767 Instruction::LoadLocal load;
4768 load.index = index;
4769 codegen->bytecodeGenerator->addInstruction(load);
4770 } else {
4771 Instruction::LoadScopedLocal load;
4772 load.index = index;
4773 load.scope = scope;
4774 codegen->bytecodeGenerator->addInstruction(load);
4775 }
4776 tdzCheck(requiresTDZCheck, throwsReferenceError);
4777 return;
4778 }
4779 case Name:
4780 if (global) {
4781 // these value properties of the global object are immutable, we we can directly convert them
4782 // to their numeric value here
4783 if (name == QStringLiteral("undefined")) {
4784 Reference::fromConst(codegen, Encode::undefined()).loadInAccumulator();
4785 return;
4786 } else if (name == QStringLiteral("Infinity")) {
4787 Reference::fromConst(codegen, Encode(qInf())).loadInAccumulator();
4788 return;
4789 } else if (name == QStringLiteral("Nan")) {
4790 Reference::fromConst(codegen, Encode(qQNaN())).loadInAccumulator();
4791 return;
4792 }
4793 }
4794
4795 if (sourceLocation.isValid())
4796 codegen->bytecodeGenerator->setLocation(sourceLocation);
4797
4798 if (global) {
4799 if (qmlGlobal) {
4800 Instruction::LoadQmlContextPropertyLookup load;
4801 load.index = codegen->registerQmlContextPropertyGetterLookup(
4802 nameAsIndex(), JSUnitGenerator::LookupForStorage);
4803 codegen->bytecodeGenerator->addInstruction(load);
4804 } else {
4805 Instruction::LoadGlobalLookup load;
4806 load.index = codegen->registerGlobalGetterLookup(
4807 nameAsIndex(), JSUnitGenerator::LookupForStorage);
4808 codegen->bytecodeGenerator->addInstruction(load);
4809 }
4810 } else {
4811 Instruction::LoadName load;
4812 load.name = nameAsIndex();
4813 codegen->bytecodeGenerator->addInstruction(load);
4814 }
4815 return;
4816 case Member:
4817 propertyBase.loadInAccumulator();
4818 tdzCheck(requiresTDZCheck, throwsReferenceError);
4819
4820 if (sourceLocation.isValid())
4821 codegen->bytecodeGenerator->setLocation(sourceLocation);
4822
4823 if (codegen->useFastLookups) {
4824 if (optionalChainJumpsToPatch && isOptional) {
4825 auto jump = codegen->bytecodeGenerator->jumpOptionalLookup(
4826 codegen->registerGetterLookup(
4827 propertyNameIndex, JSUnitGenerator::LookupForStorage));
4828 optionalChainJumpsToPatch->emplace_back(std::move(jump));
4829 } else {
4830 Instruction::GetLookup load;
4831 load.index = codegen->registerGetterLookup(
4832 propertyNameIndex, JSUnitGenerator::LookupForStorage);
4833 codegen->bytecodeGenerator->addInstruction(load);
4834 }
4835 } else {
4836 if (optionalChainJumpsToPatch && isOptional) {
4837 auto jump = codegen->bytecodeGenerator->jumpOptionalProperty(propertyNameIndex);
4838 optionalChainJumpsToPatch->emplace_back(std::move(jump));
4839 } else {
4840 Instruction::LoadProperty load;
4841 load.name = propertyNameIndex;
4842 codegen->bytecodeGenerator->addInstruction(load);
4843 }
4844 }
4845 return;
4846 case Import: {
4847 Instruction::LoadImport load;
4848 load.index = index;
4849 codegen->bytecodeGenerator->addInstruction(load);
4850 tdzCheck(requiresTDZCheck, throwsReferenceError);
4851 } return;
4852 case Subscript: {
4853 tdzCheckStackSlot(elementBase, requiresTDZCheck, throwsReferenceError);
4854 elementSubscript.loadInAccumulator();
4855 tdzCheck(subscriptRequiresTDZCheck, false);
4856 Instruction::LoadElement load;
4857 load.base = elementBase;
4858 codegen->bytecodeGenerator->addInstruction(load);
4859 } return;
4860 case Invalid:
4861 break;
4862 }
4863 Q_UNREACHABLE();
4864}
4865
4866QT_END_NAMESPACE
Codegen::VolatileMemoryLocations scan(AST::Node *s)
bool visit(ArrayMemberExpression *) override
Definition qjsvalue.h:23
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
static Node * completionStatement(StatementList *list)
static CompletionState completionState(StatementList *list)
static QSOperator::Op baseOp(int op)
CompletionState
static bool endsWithReturn(Module *module, Node *node)
static void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGenerator, const Statement *body, const SourceLocation &fallback)
static constexpr const QLatin1StringView s_globalNames[]