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
26QT_BEGIN_NAMESPACE
27
28class QQmlJSCompilePass : public QV4::Moth::ByteCodeHandler
29{
30 Q_DISABLE_COPY_MOVE(QQmlJSCompilePass)
31public:
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 &reg)
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 &reg)
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
204protected:
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
469QT_END_NAMESPACE
470
471#endif // QQMLJSCOMPILEPASS_P_H
472

source code of qtdeclarative/src/qmlcompiler/qqmljscompilepass_p.h