1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #ifndef QQMLJSCOMPILEPASS_P_H |
5 | #define QQMLJSCOMPILEPASS_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | |
17 | |
18 | #include <private/qqmljslogger_p.h> |
19 | #include <private/qqmljsregistercontent_p.h> |
20 | #include <private/qqmljsscope_p.h> |
21 | #include <private/qqmljstyperesolver_p.h> |
22 | #include <private/qv4bytecodehandler_p.h> |
23 | #include <private/qv4compiler_p.h> |
24 | #include <private/qflatmap_p.h> |
25 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler |
29 | { |
30 | Q_DISABLE_COPY_MOVE(QQmlJSCompilePass) |
31 | public: |
32 | enum RegisterShortcuts { |
33 | InvalidRegister = -1, |
34 | Accumulator = QV4::CallData::Accumulator, |
35 | This = QV4::CallData::This, |
36 | FirstArgument = QV4::CallData::OffsetCount |
37 | }; |
38 | |
39 | using SourceLocationTable = QV4::Compiler::Context::SourceLocationTable; |
40 | |
41 | struct VirtualRegister |
42 | { |
43 | QQmlJSRegisterContent content; |
44 | bool canMove = false; |
45 | bool affectedBySideEffects = false; |
46 | |
47 | private: |
48 | friend bool operator==(const VirtualRegister &a, const VirtualRegister &b) |
49 | { |
50 | return a.content == b.content && a.canMove == b.canMove |
51 | && a.affectedBySideEffects == b.affectedBySideEffects; |
52 | } |
53 | }; |
54 | |
55 | // map from register index to expected type |
56 | using VirtualRegisters = QFlatMap<int, VirtualRegister>; |
57 | |
58 | struct BasicBlock |
59 | { |
60 | QList<int> jumpOrigins; |
61 | QList<int> readRegisters; |
62 | QList<QQmlJSScope::ConstPtr> readTypes; |
63 | int jumpTarget = -1; |
64 | bool jumpIsUnconditional = false; |
65 | bool isReturnBlock = false; |
66 | bool isThrowBlock = false; |
67 | }; |
68 | |
69 | using BasicBlocks = QFlatMap<int, BasicBlock>; |
70 | |
71 | struct InstructionAnnotation |
72 | { |
73 | // Registers explicit read as part of the instruction. |
74 | VirtualRegisters readRegisters; |
75 | |
76 | // Registers that have to be converted for future instructions after a jump. |
77 | VirtualRegisters typeConversions; |
78 | |
79 | QQmlJSRegisterContent changedRegister; |
80 | int changedRegisterIndex = InvalidRegister; |
81 | bool hasSideEffects = false; |
82 | bool isRename = false; |
83 | }; |
84 | |
85 | using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>; |
86 | struct BlocksAndAnnotations |
87 | { |
88 | BasicBlocks basicBlocks; |
89 | InstructionAnnotations annotations; |
90 | }; |
91 | |
92 | struct Function |
93 | { |
94 | QQmlJSScopesById addressableScopes; |
95 | QList<QQmlJSRegisterContent> argumentTypes; |
96 | QList<QQmlJSRegisterContent> registerTypes; |
97 | QQmlJSRegisterContent returnType; |
98 | QQmlJSScope::ConstPtr qmlScope; |
99 | QByteArray code; |
100 | const SourceLocationTable *sourceLocations = nullptr; |
101 | bool isSignalHandler = false; |
102 | bool isQPropertyBinding = false; |
103 | bool isProperty = false; |
104 | bool isFullyTyped = false; |
105 | }; |
106 | |
107 | struct ObjectOrArrayDefinition |
108 | { |
109 | enum { |
110 | ArrayClassId = -1, |
111 | ArrayConstruct1ArgId = -2, |
112 | }; |
113 | |
114 | int instructionOffset = -1; |
115 | int internalClassId = ArrayClassId; |
116 | int argc = 0; |
117 | int argv = -1; |
118 | }; |
119 | |
120 | struct State |
121 | { |
122 | VirtualRegisters registers; |
123 | VirtualRegisters lookups; |
124 | |
125 | /*! |
126 | \internal |
127 | \brief The accumulatorIn is the input register of the current instruction. |
128 | |
129 | It holds a content, a type that content is acctually stored in, and an enclosing type |
130 | of the stored type called the scope. Note that passes after the original type |
131 | propagation may change the type of this register to a different type that the original |
132 | one can be coerced to. Therefore, when analyzing the same instruction in a later pass, |
133 | the type may differ from what was seen or requested ealier. See \l {readAccumulator()}. |
134 | The input type may then need to be converted to the expected type. |
135 | */ |
136 | const QQmlJSRegisterContent &accumulatorIn() const |
137 | { |
138 | auto it = registers.find(key: Accumulator); |
139 | Q_ASSERT(it != registers.end()); |
140 | return it.value().content; |
141 | }; |
142 | |
143 | /*! |
144 | \internal |
145 | \brief The accumulatorOut is the output register of the current instruction. |
146 | */ |
147 | const QQmlJSRegisterContent &accumulatorOut() const |
148 | { |
149 | Q_ASSERT(m_changedRegisterIndex == Accumulator); |
150 | return m_changedRegister; |
151 | }; |
152 | |
153 | void setRegister(int registerIndex, QQmlJSRegisterContent content) |
154 | { |
155 | const int lookupIndex = content.resultLookupIndex(); |
156 | if (lookupIndex != QQmlJSRegisterContent::InvalidLookupIndex) |
157 | lookups[lookupIndex] = { .content: content, .canMove: false, .affectedBySideEffects: false }; |
158 | |
159 | m_changedRegister = std::move(content); |
160 | m_changedRegisterIndex = registerIndex; |
161 | } |
162 | |
163 | void clearChangedRegister() |
164 | { |
165 | m_changedRegisterIndex = InvalidRegister; |
166 | m_changedRegister = QQmlJSRegisterContent(); |
167 | } |
168 | |
169 | int changedRegisterIndex() const { return m_changedRegisterIndex; } |
170 | const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; } |
171 | |
172 | void addReadRegister(int registerIndex, const QQmlJSRegisterContent ®) |
173 | { |
174 | Q_ASSERT(isRename() || reg.isConversion()); |
175 | const VirtualRegister &source = registers[registerIndex]; |
176 | VirtualRegister &target = m_readRegisters[registerIndex]; |
177 | target.content = reg; |
178 | target.canMove = source.canMove; |
179 | target.affectedBySideEffects = source.affectedBySideEffects; |
180 | } |
181 | |
182 | void addReadAccumulator(const QQmlJSRegisterContent ®) |
183 | { |
184 | addReadRegister(registerIndex: Accumulator, reg); |
185 | } |
186 | |
187 | VirtualRegisters takeReadRegisters() const { return std::move(m_readRegisters); } |
188 | void setReadRegisters(VirtualRegisters readReagisters) |
189 | { |
190 | m_readRegisters = std::move(readReagisters); |
191 | } |
192 | |
193 | QQmlJSRegisterContent readRegister(int registerIndex) const |
194 | { |
195 | Q_ASSERT(m_readRegisters.contains(registerIndex)); |
196 | return m_readRegisters[registerIndex].content; |
197 | } |
198 | |
199 | bool canMoveReadRegister(int registerIndex) const |
200 | { |
201 | auto it = m_readRegisters.find(key: registerIndex); |
202 | return it != m_readRegisters.end() && it->second.canMove; |
203 | } |
204 | |
205 | bool isRegisterAffectedBySideEffects(int registerIndex) const |
206 | { |
207 | auto it = m_readRegisters.find(key: registerIndex); |
208 | return it != m_readRegisters.end() && it->second.affectedBySideEffects; |
209 | } |
210 | |
211 | /*! |
212 | \internal |
213 | \brief The readAccumulator is the register content expected by the current instruction. |
214 | |
215 | It may differ from the actual input type of the accumulatorIn register and usage of the |
216 | value may require a conversion. |
217 | */ |
218 | QQmlJSRegisterContent readAccumulator() const |
219 | { |
220 | return readRegister(registerIndex: Accumulator); |
221 | } |
222 | |
223 | bool readsRegister(int registerIndex) const |
224 | { |
225 | return m_readRegisters.contains(key: registerIndex); |
226 | } |
227 | |
228 | bool hasSideEffects() const { return m_hasSideEffects; } |
229 | |
230 | void markSideEffects(bool hasSideEffects) { m_hasSideEffects = hasSideEffects; } |
231 | void applySideEffects(bool hasSideEffects) |
232 | { |
233 | if (!hasSideEffects) |
234 | return; |
235 | |
236 | for (auto it = registers.begin(), end = registers.end(); it != end; ++it) |
237 | it.value().affectedBySideEffects = true; |
238 | |
239 | for (auto it = lookups.begin(), end = lookups.end(); it != end; ++it) |
240 | it.value().affectedBySideEffects = true; |
241 | } |
242 | |
243 | void setHasSideEffects(bool hasSideEffects) { |
244 | markSideEffects(hasSideEffects); |
245 | applySideEffects(hasSideEffects); |
246 | } |
247 | |
248 | bool isRename() const { return m_isRename; } |
249 | void setIsRename(bool isRename) { m_isRename = isRename; } |
250 | |
251 | int renameSourceRegisterIndex() const |
252 | { |
253 | Q_ASSERT(m_isRename); |
254 | Q_ASSERT(m_readRegisters.size() == 1); |
255 | return m_readRegisters.begin().key(); |
256 | } |
257 | |
258 | private: |
259 | VirtualRegisters m_readRegisters; |
260 | QQmlJSRegisterContent m_changedRegister; |
261 | int m_changedRegisterIndex = InvalidRegister; |
262 | bool m_hasSideEffects = false; |
263 | bool m_isRename = false; |
264 | }; |
265 | |
266 | QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator, |
267 | const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger, |
268 | BasicBlocks basicBlocks = {}, InstructionAnnotations annotations = {}) |
269 | : m_jsUnitGenerator(jsUnitGenerator) |
270 | , m_typeResolver(typeResolver) |
271 | , m_logger(logger) |
272 | , m_basicBlocks(basicBlocks) |
273 | , m_annotations(annotations) |
274 | {} |
275 | |
276 | protected: |
277 | const QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr; |
278 | const QQmlJSTypeResolver *m_typeResolver = nullptr; |
279 | QQmlJSLogger *m_logger = nullptr; |
280 | |
281 | const Function *m_function = nullptr; |
282 | BasicBlocks m_basicBlocks; |
283 | InstructionAnnotations m_annotations; |
284 | QQmlJS::DiagnosticMessage *m_error = nullptr; |
285 | |
286 | int firstRegisterIndex() const |
287 | { |
288 | return FirstArgument + m_function->argumentTypes.size(); |
289 | } |
290 | |
291 | bool isArgument(int registerIndex) const |
292 | { |
293 | return registerIndex >= FirstArgument && registerIndex < firstRegisterIndex(); |
294 | } |
295 | |
296 | QQmlJSRegisterContent argumentType(int registerIndex) const |
297 | { |
298 | Q_ASSERT(isArgument(registerIndex)); |
299 | return m_function->argumentTypes[registerIndex - FirstArgument]; |
300 | } |
301 | |
302 | |
303 | State initialState(const Function *function) |
304 | { |
305 | State state; |
306 | for (int i = 0, end = function->argumentTypes.size(); i < end; ++i) { |
307 | state.registers[FirstArgument + i].content = function->argumentTypes.at(i); |
308 | Q_ASSERT(state.registers[FirstArgument + i].content.isValid()); |
309 | } |
310 | for (int i = 0, end = function->registerTypes.size(); i != end; ++i) |
311 | state.registers[firstRegisterIndex() + i].content = function->registerTypes[i]; |
312 | return state; |
313 | } |
314 | |
315 | State nextStateFromAnnotations( |
316 | const State &oldState, const InstructionAnnotations &annotations) |
317 | { |
318 | State newState; |
319 | |
320 | const auto instruction = annotations.find(key: currentInstructionOffset()); |
321 | newState.registers = oldState.registers; |
322 | newState.lookups = oldState.lookups; |
323 | |
324 | // Usually the initial accumulator type is the output of the previous instruction, but ... |
325 | if (oldState.changedRegisterIndex() != InvalidRegister) { |
326 | newState.registers[oldState.changedRegisterIndex()].affectedBySideEffects = false; |
327 | newState.registers[oldState.changedRegisterIndex()].content |
328 | = oldState.changedRegister(); |
329 | } |
330 | |
331 | // Side effects are applied at the end of an instruction: An instruction with side |
332 | // effects can still read its registers before the side effects happen. |
333 | newState.applySideEffects(hasSideEffects: oldState.hasSideEffects()); |
334 | |
335 | if (instruction == annotations.constEnd()) |
336 | return newState; |
337 | |
338 | newState.markSideEffects(hasSideEffects: instruction->second.hasSideEffects); |
339 | newState.setReadRegisters(instruction->second.readRegisters); |
340 | newState.setIsRename(instruction->second.isRename); |
341 | |
342 | for (auto it = instruction->second.typeConversions.begin(), |
343 | end = instruction->second.typeConversions.end(); it != end; ++it) { |
344 | Q_ASSERT(it.key() != InvalidRegister); |
345 | newState.registers[it.key()] = it.value(); |
346 | } |
347 | |
348 | if (instruction->second.changedRegisterIndex != InvalidRegister) { |
349 | newState.setRegister(registerIndex: instruction->second.changedRegisterIndex, |
350 | content: instruction->second.changedRegister); |
351 | } |
352 | |
353 | return newState; |
354 | } |
355 | |
356 | QQmlJS::SourceLocation sourceLocation(int instructionOffset) const |
357 | { |
358 | Q_ASSERT(m_function); |
359 | Q_ASSERT(m_function->sourceLocations); |
360 | const auto &entries = m_function->sourceLocations->entries; |
361 | auto item = std::lower_bound(first: entries.begin(), last: entries.end(), val: instructionOffset, |
362 | comp: [](auto entry, uint offset) { return entry.offset < offset; }); |
363 | |
364 | Q_ASSERT(item != entries.end()); |
365 | return item->location; |
366 | } |
367 | |
368 | QQmlJS::SourceLocation currentSourceLocation() const |
369 | { |
370 | return sourceLocation(instructionOffset: currentInstructionOffset()); |
371 | } |
372 | |
373 | void setError(const QString &message, int instructionOffset) |
374 | { |
375 | Q_ASSERT(m_error); |
376 | if (m_error->isValid()) |
377 | return; |
378 | m_error->message = message; |
379 | m_error->loc = sourceLocation(instructionOffset); |
380 | } |
381 | |
382 | void setError(const QString &message) |
383 | { |
384 | setError(message, instructionOffset: currentInstructionOffset()); |
385 | } |
386 | |
387 | static bool instructionManipulatesContext(QV4::Moth::Instr::Type type) |
388 | { |
389 | using Type = QV4::Moth::Instr::Type; |
390 | switch (type) { |
391 | case Type::PopContext: |
392 | case Type::PopScriptContext: |
393 | case Type::CreateCallContext: |
394 | case Type::CreateCallContext_Wide: |
395 | case Type::PushCatchContext: |
396 | case Type::PushCatchContext_Wide: |
397 | case Type::PushWithContext: |
398 | case Type::PushWithContext_Wide: |
399 | case Type::PushBlockContext: |
400 | case Type::PushBlockContext_Wide: |
401 | case Type::CloneBlockContext: |
402 | case Type::CloneBlockContext_Wide: |
403 | case Type::PushScriptContext: |
404 | case Type::PushScriptContext_Wide: |
405 | return true; |
406 | default: |
407 | break; |
408 | } |
409 | return false; |
410 | } |
411 | |
412 | // Stub out all the methods so that passes can choose to only implement part of them. |
413 | void generate_Add(int) override {} |
414 | void generate_As(int) override {} |
415 | void generate_BitAnd(int) override {} |
416 | void generate_BitAndConst(int) override {} |
417 | void generate_BitOr(int) override {} |
418 | void generate_BitOrConst(int) override {} |
419 | void generate_BitXor(int) override {} |
420 | void generate_BitXorConst(int) override {} |
421 | void generate_CallGlobalLookup(int, int, int) override {} |
422 | void generate_CallName(int, int, int) override {} |
423 | void generate_CallPossiblyDirectEval(int, int) override {} |
424 | void generate_CallProperty(int, int, int, int) override {} |
425 | void generate_CallPropertyLookup(int, int, int, int) override {} |
426 | void generate_CallQmlContextPropertyLookup(int, int, int) override {} |
427 | void generate_CallValue(int, int, int) override {} |
428 | void generate_CallWithReceiver(int, int, int, int) override {} |
429 | void generate_CallWithSpread(int, int, int, int) override {} |
430 | void generate_CheckException() override {} |
431 | void generate_CloneBlockContext() override {} |
432 | void generate_CmpEq(int) override {} |
433 | void generate_CmpEqInt(int) override {} |
434 | void generate_CmpEqNull() override {} |
435 | void generate_CmpGe(int) override {} |
436 | void generate_CmpGt(int) override {} |
437 | void generate_CmpIn(int) override {} |
438 | void generate_CmpInstanceOf(int) override {} |
439 | void generate_CmpLe(int) override {} |
440 | void generate_CmpLt(int) override {} |
441 | void generate_CmpNe(int) override {} |
442 | void generate_CmpNeInt(int) override {} |
443 | void generate_CmpNeNull() override {} |
444 | void generate_CmpStrictEqual(int) override {} |
445 | void generate_CmpStrictNotEqual(int) override {} |
446 | void generate_Construct(int, int, int) override {} |
447 | void generate_ConstructWithSpread(int, int, int) override {} |
448 | void generate_ConvertThisToObject() override {} |
449 | void generate_CreateCallContext() override {} |
450 | void generate_CreateClass(int, int, int) override {} |
451 | void generate_CreateMappedArgumentsObject() override {} |
452 | void generate_CreateRestParameter(int) override {} |
453 | void generate_CreateUnmappedArgumentsObject() override {} |
454 | void generate_DeadTemporalZoneCheck(int) override {} |
455 | void generate_Debug() override {} |
456 | void generate_DeclareVar(int, int) override {} |
457 | void generate_Decrement() override {} |
458 | void generate_DefineArray(int, int) override {} |
459 | void generate_DefineObjectLiteral(int, int, int) override {} |
460 | void generate_DeleteName(int) override {} |
461 | void generate_DeleteProperty(int, int) override {} |
462 | void generate_DestructureRestElement() override {} |
463 | void generate_Div(int) override {} |
464 | void generate_Exp(int) override {} |
465 | void generate_GetException() override {} |
466 | void generate_GetIterator(int) override {} |
467 | void generate_GetLookup(int) override {} |
468 | void generate_GetOptionalLookup(int, int) override {} |
469 | void generate_GetTemplateObject(int) override {} |
470 | void generate_Increment() override {} |
471 | void generate_InitializeBlockDeadTemporalZone(int, int) override {} |
472 | void generate_IteratorClose() override {} |
473 | void generate_IteratorNext(int, int) override {} |
474 | void generate_IteratorNextForYieldStar(int, int, int) override {} |
475 | void generate_Jump(int) override {} |
476 | void generate_JumpFalse(int) override {} |
477 | void generate_JumpNoException(int) override {} |
478 | void generate_JumpNotUndefined(int) override {} |
479 | void generate_JumpTrue(int) override {} |
480 | void generate_LoadClosure(int) override {} |
481 | void generate_LoadConst(int) override {} |
482 | void generate_LoadElement(int) override {} |
483 | void generate_LoadFalse() override {} |
484 | void generate_LoadGlobalLookup(int) override {} |
485 | void generate_LoadImport(int) override {} |
486 | void generate_LoadInt(int) override {} |
487 | void generate_LoadLocal(int) override {} |
488 | void generate_LoadName(int) override {} |
489 | void generate_LoadNull() override {} |
490 | void generate_LoadOptionalProperty(int, int) override {} |
491 | void generate_LoadProperty(int) override {} |
492 | void generate_LoadQmlContextPropertyLookup(int) override {} |
493 | void generate_LoadReg(int) override {} |
494 | void generate_LoadRuntimeString(int) override {} |
495 | void generate_LoadScopedLocal(int, int) override {} |
496 | void generate_LoadSuperConstructor() override {} |
497 | void generate_LoadSuperProperty(int) override {} |
498 | void generate_LoadTrue() override {} |
499 | void generate_LoadUndefined() override {} |
500 | void generate_LoadZero() override {} |
501 | void generate_Mod(int) override {} |
502 | void generate_MoveConst(int, int) override {} |
503 | void generate_MoveReg(int, int) override {} |
504 | void generate_MoveRegExp(int, int) override {} |
505 | void generate_Mul(int) override {} |
506 | void generate_PopContext() override {} |
507 | void generate_PopScriptContext() override {} |
508 | void generate_PushBlockContext(int) override {} |
509 | void generate_PushCatchContext(int, int) override {} |
510 | void generate_PushScriptContext(int) override {} |
511 | void generate_PushWithContext() override {} |
512 | void generate_Resume(int) override {} |
513 | void generate_Ret() override {} |
514 | void generate_SetException() override {} |
515 | void generate_SetLookup(int, int) override {} |
516 | void generate_SetUnwindHandler(int) override {} |
517 | void generate_Shl(int) override {} |
518 | void generate_ShlConst(int) override {} |
519 | void generate_Shr(int) override {} |
520 | void generate_ShrConst(int) override {} |
521 | void generate_StoreElement(int, int) override {} |
522 | void generate_StoreLocal(int) override {} |
523 | void generate_StoreNameSloppy(int) override {} |
524 | void generate_StoreNameStrict(int) override {} |
525 | void generate_StoreProperty(int, int) override {} |
526 | void generate_StoreReg(int) override {} |
527 | void generate_StoreScopedLocal(int, int) override {} |
528 | void generate_StoreSuperProperty(int) override {} |
529 | void generate_Sub(int) override {} |
530 | void generate_TailCall(int, int, int, int) override {} |
531 | void generate_ThrowException() override {} |
532 | void generate_ThrowOnNullOrUndefined() override {} |
533 | void generate_ToObject() override {} |
534 | void generate_TypeofName(int) override {} |
535 | void generate_TypeofValue() override {} |
536 | void generate_UCompl() override {} |
537 | void generate_UMinus() override {} |
538 | void generate_UNot() override {} |
539 | void generate_UPlus() override {} |
540 | void generate_UShr(int) override {} |
541 | void generate_UShrConst(int) override {} |
542 | void generate_UnwindDispatch() override {} |
543 | void generate_UnwindToLabel(int, int) override {} |
544 | void generate_Yield() override {} |
545 | void generate_YieldStar() override {} |
546 | }; |
547 | |
548 | QT_END_NAMESPACE |
549 | |
550 | #endif // QQMLJSCOMPILEPASS_P_H |
551 |
Definitions
- QQmlJSCompilePass
- QQmlJSCompilePass
- RegisterShortcuts
- VirtualRegister
- operator==
- BasicBlock
- InstructionAnnotation
- BlocksAndAnnotations
- Function
- ObjectOrArrayDefinition
- State
- accumulatorIn
- accumulatorOut
- setRegister
- clearChangedRegister
- changedRegisterIndex
- changedRegister
- addReadRegister
- addReadAccumulator
- takeReadRegisters
- setReadRegisters
- readRegister
- canMoveReadRegister
- isRegisterAffectedBySideEffects
- readAccumulator
- readsRegister
- hasSideEffects
- markSideEffects
- applySideEffects
- setHasSideEffects
- isRename
- setIsRename
- renameSourceRegisterIndex
- QQmlJSCompilePass
- firstRegisterIndex
- isArgument
- argumentType
- initialState
- nextStateFromAnnotations
- sourceLocation
- currentSourceLocation
- setError
- setError
- instructionManipulatesContext
- generate_Add
- generate_As
- generate_BitAnd
- generate_BitAndConst
- generate_BitOr
- generate_BitOrConst
- generate_BitXor
- generate_BitXorConst
- generate_CallGlobalLookup
- generate_CallName
- generate_CallPossiblyDirectEval
- generate_CallProperty
- generate_CallPropertyLookup
- generate_CallQmlContextPropertyLookup
- generate_CallValue
- generate_CallWithReceiver
- generate_CallWithSpread
- generate_CheckException
- generate_CloneBlockContext
- generate_CmpEq
- generate_CmpEqInt
- generate_CmpEqNull
- generate_CmpGe
- generate_CmpGt
- generate_CmpIn
- generate_CmpInstanceOf
- generate_CmpLe
- generate_CmpLt
- generate_CmpNe
- generate_CmpNeInt
- generate_CmpNeNull
- generate_CmpStrictEqual
- generate_CmpStrictNotEqual
- generate_Construct
- generate_ConstructWithSpread
- generate_ConvertThisToObject
- generate_CreateCallContext
- generate_CreateClass
- generate_CreateMappedArgumentsObject
- generate_CreateRestParameter
- generate_CreateUnmappedArgumentsObject
- generate_DeadTemporalZoneCheck
- generate_Debug
- generate_DeclareVar
- generate_Decrement
- generate_DefineArray
- generate_DefineObjectLiteral
- generate_DeleteName
- generate_DeleteProperty
- generate_DestructureRestElement
- generate_Div
- generate_Exp
- generate_GetException
- generate_GetIterator
- generate_GetLookup
- generate_GetOptionalLookup
- generate_GetTemplateObject
- generate_Increment
- generate_InitializeBlockDeadTemporalZone
- generate_IteratorClose
- generate_IteratorNext
- generate_IteratorNextForYieldStar
- generate_Jump
- generate_JumpFalse
- generate_JumpNoException
- generate_JumpNotUndefined
- generate_JumpTrue
- generate_LoadClosure
- generate_LoadConst
- generate_LoadElement
- generate_LoadFalse
- generate_LoadGlobalLookup
- generate_LoadImport
- generate_LoadInt
- generate_LoadLocal
- generate_LoadName
- generate_LoadNull
- generate_LoadOptionalProperty
- generate_LoadProperty
- generate_LoadQmlContextPropertyLookup
- generate_LoadReg
- generate_LoadRuntimeString
- generate_LoadScopedLocal
- generate_LoadSuperConstructor
- generate_LoadSuperProperty
- generate_LoadTrue
- generate_LoadUndefined
- generate_LoadZero
- generate_Mod
- generate_MoveConst
- generate_MoveReg
- generate_MoveRegExp
- generate_Mul
- generate_PopContext
- generate_PopScriptContext
- generate_PushBlockContext
- generate_PushCatchContext
- generate_PushScriptContext
- generate_PushWithContext
- generate_Resume
- generate_Ret
- generate_SetException
- generate_SetLookup
- generate_SetUnwindHandler
- generate_Shl
- generate_ShlConst
- generate_Shr
- generate_ShrConst
- generate_StoreElement
- generate_StoreLocal
- generate_StoreNameSloppy
- generate_StoreNameStrict
- generate_StoreProperty
- generate_StoreReg
- generate_StoreScopedLocal
- generate_StoreSuperProperty
- generate_Sub
- generate_TailCall
- generate_ThrowException
- generate_ThrowOnNullOrUndefined
- generate_ToObject
- generate_TypeofName
- generate_TypeofValue
- generate_UCompl
- generate_UMinus
- generate_UNot
- generate_UPlus
- generate_UShr
- generate_UShrConst
- generate_UnwindDispatch
- generate_UnwindToLabel
- generate_Yield
Start learning QML with our Intro Training
Find out more