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