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