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