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

source code of qtdeclarative/src/qml/compiler/qv4codegen.cpp