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