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