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