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