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