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 InstructionAnnotation |
59 | { |
60 | // Registers explicit read as part of the instruction. |
61 | VirtualRegisters readRegisters; |
62 | |
63 | // Registers that have to be converted for future instructions after a jump. |
64 | VirtualRegisters typeConversions; |
65 | |
66 | QQmlJSRegisterContent changedRegister; |
67 | int changedRegisterIndex = InvalidRegister; |
68 | bool hasSideEffects = false; |
69 | bool isRename = false; |
70 | }; |
71 | |
72 | using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>; |
73 | |
74 | struct Function |
75 | { |
76 | QQmlJSScopesById addressableScopes; |
77 | QList<QQmlJSRegisterContent> argumentTypes; |
78 | QList<QQmlJSRegisterContent> registerTypes; |
79 | QQmlJSScope::ConstPtr returnType; |
80 | QQmlJSScope::ConstPtr qmlScope; |
81 | QByteArray code; |
82 | const SourceLocationTable *sourceLocations = nullptr; |
83 | bool isSignalHandler = false; |
84 | bool isQPropertyBinding = false; |
85 | bool isProperty = false; |
86 | }; |
87 | |
88 | struct State |
89 | { |
90 | VirtualRegisters registers; |
91 | |
92 | const QQmlJSRegisterContent &accumulatorIn() const |
93 | { |
94 | auto it = registers.find(key: Accumulator); |
95 | Q_ASSERT(it != registers.end()); |
96 | return it.value().content; |
97 | }; |
98 | |
99 | const QQmlJSRegisterContent &accumulatorOut() const |
100 | { |
101 | Q_ASSERT(m_changedRegisterIndex == Accumulator); |
102 | return m_changedRegister; |
103 | }; |
104 | |
105 | void setRegister(int registerIndex, QQmlJSRegisterContent content) |
106 | { |
107 | m_changedRegister = std::move(content); |
108 | m_changedRegisterIndex = registerIndex; |
109 | } |
110 | |
111 | void clearChangedRegister() |
112 | { |
113 | m_changedRegisterIndex = InvalidRegister; |
114 | m_changedRegister = QQmlJSRegisterContent(); |
115 | } |
116 | |
117 | int changedRegisterIndex() const { return m_changedRegisterIndex; } |
118 | const QQmlJSRegisterContent &changedRegister() const { return m_changedRegister; } |
119 | |
120 | void addReadRegister(int registerIndex, const QQmlJSRegisterContent ®) |
121 | { |
122 | Q_ASSERT(isRename() || reg.isConversion()); |
123 | const VirtualRegister &source = registers[registerIndex]; |
124 | VirtualRegister &target = m_readRegisters[registerIndex]; |
125 | target.content = reg; |
126 | target.canMove = source.canMove; |
127 | target.affectedBySideEffects = source.affectedBySideEffects; |
128 | } |
129 | |
130 | void addReadAccumulator(const QQmlJSRegisterContent ®) |
131 | { |
132 | addReadRegister(registerIndex: Accumulator, reg); |
133 | } |
134 | |
135 | VirtualRegisters takeReadRegisters() const { return std::move(m_readRegisters); } |
136 | void setReadRegisters(VirtualRegisters readReagisters) |
137 | { |
138 | m_readRegisters = std::move(readReagisters); |
139 | } |
140 | |
141 | QQmlJSRegisterContent readRegister(int registerIndex) const |
142 | { |
143 | Q_ASSERT(m_readRegisters.contains(registerIndex)); |
144 | return m_readRegisters[registerIndex].content; |
145 | } |
146 | |
147 | bool canMoveReadRegister(int registerIndex) const |
148 | { |
149 | auto it = m_readRegisters.find(key: registerIndex); |
150 | return it != m_readRegisters.end() && it->second.canMove; |
151 | } |
152 | |
153 | bool isRegisterAffectedBySideEffects(int registerIndex) const |
154 | { |
155 | auto it = m_readRegisters.find(key: registerIndex); |
156 | return it != m_readRegisters.end() && it->second.affectedBySideEffects; |
157 | } |
158 | |
159 | QQmlJSRegisterContent readAccumulator() const |
160 | { |
161 | return readRegister(registerIndex: Accumulator); |
162 | } |
163 | |
164 | bool readsRegister(int registerIndex) const |
165 | { |
166 | return m_readRegisters.contains(key: registerIndex); |
167 | } |
168 | |
169 | bool hasSideEffects() const { return m_hasSideEffects; } |
170 | void setHasSideEffects(bool hasSideEffects) { |
171 | m_hasSideEffects = hasSideEffects; |
172 | if (!hasSideEffects) |
173 | return; |
174 | |
175 | for (auto it = registers.begin(), end = registers.end(); it != end; ++it) |
176 | it.value().affectedBySideEffects = true; |
177 | } |
178 | |
179 | bool isRename() const { return m_isRename; } |
180 | void setIsRename(bool isRename) { m_isRename = isRename; } |
181 | |
182 | int renameSourceRegisterIndex() const |
183 | { |
184 | Q_ASSERT(m_isRename); |
185 | Q_ASSERT(m_readRegisters.size() == 1); |
186 | return m_readRegisters.begin().key(); |
187 | } |
188 | |
189 | private: |
190 | VirtualRegisters m_readRegisters; |
191 | QQmlJSRegisterContent m_changedRegister; |
192 | int m_changedRegisterIndex = InvalidRegister; |
193 | bool m_hasSideEffects = false; |
194 | bool m_isRename = false; |
195 | }; |
196 | |
197 | QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator, |
198 | const QQmlJSTypeResolver *typeResolver, QQmlJSLogger *logger) |
199 | : m_jsUnitGenerator(jsUnitGenerator) |
200 | , m_typeResolver(typeResolver) |
201 | , m_logger(logger) |
202 | {} |
203 | |
204 | protected: |
205 | const QV4::Compiler::JSUnitGenerator *m_jsUnitGenerator = nullptr; |
206 | const QQmlJSTypeResolver *m_typeResolver = nullptr; |
207 | QQmlJSLogger *m_logger = nullptr; |
208 | |
209 | const Function *m_function = nullptr; |
210 | QQmlJS::DiagnosticMessage *m_error = nullptr; |
211 | |
212 | int firstRegisterIndex() const |
213 | { |
214 | return FirstArgument + m_function->argumentTypes.size(); |
215 | } |
216 | |
217 | bool isArgument(int registerIndex) const |
218 | { |
219 | return registerIndex >= FirstArgument && registerIndex < firstRegisterIndex(); |
220 | } |
221 | |
222 | QQmlJSRegisterContent argumentType(int registerIndex) const |
223 | { |
224 | Q_ASSERT(isArgument(registerIndex)); |
225 | return m_function->argumentTypes[registerIndex - FirstArgument]; |
226 | } |
227 | |
228 | |
229 | State initialState(const Function *function) |
230 | { |
231 | State state; |
232 | for (int i = 0, end = function->argumentTypes.size(); i < end; ++i) { |
233 | state.registers[FirstArgument + i].content = function->argumentTypes.at(i); |
234 | Q_ASSERT(state.registers[FirstArgument + i].content.isValid()); |
235 | } |
236 | for (int i = 0, end = function->registerTypes.size(); i != end; ++i) |
237 | state.registers[firstRegisterIndex() + i].content = function->registerTypes[i]; |
238 | return state; |
239 | } |
240 | |
241 | State nextStateFromAnnotations( |
242 | const State &oldState, const InstructionAnnotations &annotations) |
243 | { |
244 | State newState; |
245 | |
246 | const auto instruction = annotations.find(key: currentInstructionOffset()); |
247 | newState.registers = oldState.registers; |
248 | |
249 | // Usually the initial accumulator type is the output of the previous instruction, but ... |
250 | if (oldState.changedRegisterIndex() != InvalidRegister) { |
251 | newState.registers[oldState.changedRegisterIndex()].affectedBySideEffects = false; |
252 | newState.registers[oldState.changedRegisterIndex()].content |
253 | = oldState.changedRegister(); |
254 | } |
255 | |
256 | if (instruction == annotations.constEnd()) |
257 | return newState; |
258 | |
259 | newState.setHasSideEffects(instruction->second.hasSideEffects); |
260 | newState.setReadRegisters(instruction->second.readRegisters); |
261 | newState.setIsRename(instruction->second.isRename); |
262 | |
263 | for (auto it = instruction->second.typeConversions.begin(), |
264 | end = instruction->second.typeConversions.end(); it != end; ++it) { |
265 | Q_ASSERT(it.key() != InvalidRegister); |
266 | newState.registers[it.key()] = it.value(); |
267 | } |
268 | |
269 | if (instruction->second.changedRegisterIndex != InvalidRegister) { |
270 | newState.setRegister(registerIndex: instruction->second.changedRegisterIndex, |
271 | content: instruction->second.changedRegister); |
272 | } |
273 | |
274 | return newState; |
275 | } |
276 | |
277 | QQmlJS::SourceLocation sourceLocation(int instructionOffset) const |
278 | { |
279 | Q_ASSERT(m_function); |
280 | Q_ASSERT(m_function->sourceLocations); |
281 | const auto &entries = m_function->sourceLocations->entries; |
282 | auto item = std::lower_bound(first: entries.begin(), last: entries.end(), val: instructionOffset, |
283 | comp: [](auto entry, uint offset) { return entry.offset < offset; }); |
284 | |
285 | Q_ASSERT(item != entries.end()); |
286 | return item->location; |
287 | } |
288 | |
289 | QQmlJS::SourceLocation currentSourceLocation() const |
290 | { |
291 | return sourceLocation(instructionOffset: currentInstructionOffset()); |
292 | } |
293 | |
294 | void setError(const QString &message, int instructionOffset) |
295 | { |
296 | Q_ASSERT(m_error); |
297 | if (m_error->isValid()) |
298 | return; |
299 | m_error->message = message; |
300 | m_error->loc = sourceLocation(instructionOffset); |
301 | } |
302 | |
303 | void setError(const QString &message) |
304 | { |
305 | setError(message, instructionOffset: currentInstructionOffset()); |
306 | } |
307 | |
308 | static bool instructionManipulatesContext(QV4::Moth::Instr::Type type) |
309 | { |
310 | using Type = QV4::Moth::Instr::Type; |
311 | switch (type) { |
312 | case Type::PopContext: |
313 | case Type::PopScriptContext: |
314 | case Type::CreateCallContext: |
315 | case Type::CreateCallContext_Wide: |
316 | case Type::PushCatchContext: |
317 | case Type::PushCatchContext_Wide: |
318 | case Type::PushWithContext: |
319 | case Type::PushWithContext_Wide: |
320 | case Type::PushBlockContext: |
321 | case Type::PushBlockContext_Wide: |
322 | case Type::CloneBlockContext: |
323 | case Type::CloneBlockContext_Wide: |
324 | case Type::PushScriptContext: |
325 | case Type::PushScriptContext_Wide: |
326 | return true; |
327 | default: |
328 | break; |
329 | } |
330 | return false; |
331 | } |
332 | |
333 | // Stub out all the methods so that passes can choose to only implement part of them. |
334 | void generate_Add(int) override {} |
335 | void generate_As(int) override {} |
336 | void generate_BitAnd(int) override {} |
337 | void generate_BitAndConst(int) override {} |
338 | void generate_BitOr(int) override {} |
339 | void generate_BitOrConst(int) override {} |
340 | void generate_BitXor(int) override {} |
341 | void generate_BitXorConst(int) override {} |
342 | void generate_CallGlobalLookup(int, int, int) override {} |
343 | void generate_CallName(int, int, int) override {} |
344 | void generate_CallPossiblyDirectEval(int, int) override {} |
345 | void generate_CallProperty(int, int, int, int) override {} |
346 | void generate_CallPropertyLookup(int, int, int, int) override {} |
347 | void generate_CallQmlContextPropertyLookup(int, int, int) override {} |
348 | void generate_CallValue(int, int, int) override {} |
349 | void generate_CallWithReceiver(int, int, int, int) override {} |
350 | void generate_CallWithSpread(int, int, int, int) override {} |
351 | void generate_CheckException() override {} |
352 | void generate_CloneBlockContext() override {} |
353 | void generate_CmpEq(int) override {} |
354 | void generate_CmpEqInt(int) override {} |
355 | void generate_CmpEqNull() override {} |
356 | void generate_CmpGe(int) override {} |
357 | void generate_CmpGt(int) override {} |
358 | void generate_CmpIn(int) override {} |
359 | void generate_CmpInstanceOf(int) override {} |
360 | void generate_CmpLe(int) override {} |
361 | void generate_CmpLt(int) override {} |
362 | void generate_CmpNe(int) override {} |
363 | void generate_CmpNeInt(int) override {} |
364 | void generate_CmpNeNull() override {} |
365 | void generate_CmpStrictEqual(int) override {} |
366 | void generate_CmpStrictNotEqual(int) override {} |
367 | void generate_Construct(int, int, int) override {} |
368 | void generate_ConstructWithSpread(int, int, int) override {} |
369 | void generate_ConvertThisToObject() override {} |
370 | void generate_CreateCallContext() override {} |
371 | void generate_CreateClass(int, int, int) override {} |
372 | void generate_CreateMappedArgumentsObject() override {} |
373 | void generate_CreateRestParameter(int) override {} |
374 | void generate_CreateUnmappedArgumentsObject() override {} |
375 | void generate_DeadTemporalZoneCheck(int) override {} |
376 | void generate_Debug() override {} |
377 | void generate_DeclareVar(int, int) override {} |
378 | void generate_Decrement() override {} |
379 | void generate_DefineArray(int, int) override {} |
380 | void generate_DefineObjectLiteral(int, int, int) override {} |
381 | void generate_DeleteName(int) override {} |
382 | void generate_DeleteProperty(int, int) override {} |
383 | void generate_DestructureRestElement() override {} |
384 | void generate_Div(int) override {} |
385 | void generate_Exp(int) override {} |
386 | void generate_GetException() override {} |
387 | void generate_GetIterator(int) override {} |
388 | void generate_GetLookup(int) override {} |
389 | void generate_GetOptionalLookup(int, int) override {} |
390 | void generate_GetTemplateObject(int) override {} |
391 | void generate_Increment() override {} |
392 | void generate_InitializeBlockDeadTemporalZone(int, int) override {} |
393 | void generate_IteratorClose(int) override {} |
394 | void generate_IteratorNext(int, int) override {} |
395 | void generate_IteratorNextForYieldStar(int, int) override {} |
396 | void generate_Jump(int) override {} |
397 | void generate_JumpFalse(int) override {} |
398 | void generate_JumpNoException(int) override {} |
399 | void generate_JumpNotUndefined(int) override {} |
400 | void generate_JumpTrue(int) override {} |
401 | void generate_LoadClosure(int) override {} |
402 | void generate_LoadConst(int) override {} |
403 | void generate_LoadElement(int) override {} |
404 | void generate_LoadFalse() override {} |
405 | void generate_LoadGlobalLookup(int) override {} |
406 | void generate_LoadImport(int) override {} |
407 | void generate_LoadInt(int) override {} |
408 | void generate_LoadLocal(int) override {} |
409 | void generate_LoadName(int) override {} |
410 | void generate_LoadNull() override {} |
411 | void generate_LoadOptionalProperty(int, int) override {} |
412 | void generate_LoadProperty(int) override {} |
413 | void generate_LoadQmlContextPropertyLookup(int) override {} |
414 | void generate_LoadReg(int) override {} |
415 | void generate_LoadRuntimeString(int) override {} |
416 | void generate_LoadScopedLocal(int, int) override {} |
417 | void generate_LoadSuperConstructor() override {} |
418 | void generate_LoadSuperProperty(int) override {} |
419 | void generate_LoadTrue() override {} |
420 | void generate_LoadUndefined() override {} |
421 | void generate_LoadZero() override {} |
422 | void generate_Mod(int) override {} |
423 | void generate_MoveConst(int, int) override {} |
424 | void generate_MoveReg(int, int) override {} |
425 | void generate_MoveRegExp(int, int) override {} |
426 | void generate_Mul(int) override {} |
427 | void generate_PopContext() override {} |
428 | void generate_PopScriptContext() override {} |
429 | void generate_PushBlockContext(int) override {} |
430 | void generate_PushCatchContext(int, int) override {} |
431 | void generate_PushScriptContext(int) override {} |
432 | void generate_PushWithContext() override {} |
433 | void generate_Resume(int) override {} |
434 | void generate_Ret() override {} |
435 | void generate_SetException() override {} |
436 | void generate_SetLookup(int, int) override {} |
437 | void generate_SetUnwindHandler(int) override {} |
438 | void generate_Shl(int) override {} |
439 | void generate_ShlConst(int) override {} |
440 | void generate_Shr(int) override {} |
441 | void generate_ShrConst(int) override {} |
442 | void generate_StoreElement(int, int) override {} |
443 | void generate_StoreLocal(int) override {} |
444 | void generate_StoreNameSloppy(int) override {} |
445 | void generate_StoreNameStrict(int) override {} |
446 | void generate_StoreProperty(int, int) override {} |
447 | void generate_StoreReg(int) override {} |
448 | void generate_StoreScopedLocal(int, int) override {} |
449 | void generate_StoreSuperProperty(int) override {} |
450 | void generate_Sub(int) override {} |
451 | void generate_TailCall(int, int, int, int) override {} |
452 | void generate_ThrowException() override {} |
453 | void generate_ThrowOnNullOrUndefined() override {} |
454 | void generate_ToObject() override {} |
455 | void generate_TypeofName(int) override {} |
456 | void generate_TypeofValue() override {} |
457 | void generate_UCompl() override {} |
458 | void generate_UMinus() override {} |
459 | void generate_UNot() override {} |
460 | void generate_UPlus() override {} |
461 | void generate_UShr(int) override {} |
462 | void generate_UShrConst(int) override {} |
463 | void generate_UnwindDispatch() override {} |
464 | void generate_UnwindToLabel(int, int) override {} |
465 | void generate_Yield() override {} |
466 | void generate_YieldStar() override {} |
467 | }; |
468 | |
469 | QT_END_NAMESPACE |
470 | |
471 | #endif // QQMLJSCOMPILEPASS_P_H |
472 | |