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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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