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#ifndef QV4COMPILERCONTEXT_P_H
4#define QV4COMPILERCONTEXT_P_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <private/qqmljsast_p.h>
18#include <private/qv4compileddata_p.h>
19#include <QtCore/QStringList>
20#include <QtCore/QDateTime>
21#include <QtCore/QStack>
22#include <QtCore/QHash>
23#include <QtCore/QMap>
24#include <QtCore/QSet>
25#include <QtCore/QVarLengthArray>
26
27#include <memory>
28
29QT_BEGIN_NAMESPACE
30
31namespace QV4 {
32
33namespace Moth {
34class BytecodeGenerator;
35}
36
37namespace Compiler {
38
39class Codegen;
40struct ControlFlow;
41
42enum class ContextType {
43 Global,
44 Function,
45 Eval,
46 Binding, // This is almost the same as Eval, except:
47 // * function declarations are moved to the return address when encountered
48 // * return statements are allowed everywhere (like in FunctionCode)
49 // * variable declarations are treated as true locals (like in FunctionCode)
50 Block,
51 ESModule,
52 ScriptImportedByQML,
53};
54
55struct Context;
56
57struct Class {
58 struct Method {
59 enum Type {
60 Regular,
61 Getter,
62 Setter
63 };
64 uint nameIndex;
65 Type type;
66 uint functionIndex;
67 };
68
69 uint nameIndex;
70 uint constructorIndex = UINT_MAX;
71 QVector<Method> staticMethods;
72 QVector<Method> methods;
73};
74
75struct TemplateObject {
76 QVector<uint> strings;
77 QVector<uint> rawStrings;
78 bool operator==(const TemplateObject &other) {
79 return strings == other.strings && rawStrings == other.rawStrings;
80 }
81};
82
83struct ExportEntry
84{
85 QString exportName;
86 QString moduleRequest;
87 QString importName;
88 QString localName;
89 CompiledData::Location location;
90
91 static bool lessThan(const ExportEntry &lhs, const ExportEntry &rhs)
92 { return lhs.exportName < rhs.exportName; }
93};
94
95struct ImportEntry
96{
97 QString moduleRequest;
98 QString importName;
99 QString localName;
100 CompiledData::Location location;
101};
102
103struct Module {
104 Module(bool debugMode)
105 : debugMode(debugMode)
106 {}
107 ~Module() {
108 qDeleteAll(c: contextMap);
109 }
110
111 Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode);
112
113 QHash<QQmlJS::AST::Node *, Context *> contextMap;
114 QList<Context *> functions;
115 QList<Context *> blocks;
116 QVector<Class> classes;
117 QVector<TemplateObject> templateObjects;
118 Context *rootContext;
119 QString fileName;
120 QString finalUrl;
121 QDateTime sourceTimeStamp;
122 uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
123 bool debugMode = false;
124 QVector<ExportEntry> localExportEntries;
125 QVector<ExportEntry> indirectExportEntries;
126 QVector<ExportEntry> starExportEntries;
127 QVector<ImportEntry> importEntries;
128 QStringList moduleRequests;
129};
130
131
132struct Context {
133 Context *parent;
134 QString name;
135 int line = 0;
136 int column = 0;
137 int registerCountInFunction = 0;
138 int functionIndex = -1;
139 int blockIndex = -1;
140
141 enum MemberType {
142 UndefinedMember,
143 ThisFunctionName,
144 VariableDefinition,
145 VariableDeclaration,
146 FunctionDefinition
147 };
148
149 struct SourceLocationTable
150 {
151 struct Entry
152 {
153 quint32 offset;
154 QQmlJS::SourceLocation location;
155 };
156 QVector<Entry> entries;
157 };
158
159 struct Member {
160 MemberType type = UndefinedMember;
161 int index = -1;
162 QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
163 mutable bool canEscape = false;
164 bool isInjected = false;
165 QQmlJS::AST::FunctionExpression *function = nullptr;
166 QQmlJS::SourceLocation declarationLocation;
167
168 bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
169 bool requiresTDZCheck(const QQmlJS::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const;
170 };
171 typedef QMap<QString, Member> MemberMap;
172
173 MemberMap members;
174 QSet<QString> usedVariables;
175 QQmlJS::AST::FormalParameterList *formals = nullptr;
176 QQmlJS::AST::BoundNames arguments;
177 QQmlJS::AST::Type *returnType = nullptr;
178 QStringList locals;
179 QStringList moduleRequests;
180 QVector<ImportEntry> importEntries;
181 QVector<ExportEntry> exportEntries;
182 QString localNameForDefaultExport;
183 QVector<Context *> nestedContexts;
184
185 ControlFlow *controlFlow = nullptr;
186 QByteArray code;
187 QVector<CompiledData::CodeOffsetToLineAndStatement> lineAndStatementNumberMapping;
188 std::unique_ptr<SourceLocationTable> sourceLocationTable;
189 std::vector<unsigned> labelInfo;
190
191 int nRegisters = 0;
192 int registerOffset = -1;
193 int sizeOfLocalTemporalDeadZone = 0;
194 int firstTemporalDeadZoneRegister = 0;
195 int sizeOfRegisterTemporalDeadZone = 0;
196 bool hasDirectEval = false;
197 bool allVarsEscape = false;
198 bool hasNestedFunctions = false;
199 bool isStrict = false;
200 bool isArrowFunction = false;
201 bool isGenerator = false;
202 bool usesThis = false;
203 bool innerFunctionAccessesThis = false;
204 bool innerFunctionAccessesNewTarget = false;
205 bool returnsClosure = false;
206 mutable bool argumentsCanEscape = false;
207 bool requiresExecutionContext = false;
208 bool isWithBlock = false;
209 bool isCatchBlock = false;
210 QString caughtVariable;
211 QQmlJS::SourceLocation lastBlockInitializerLocation;
212
213 enum UsesArgumentsObject {
214 ArgumentsObjectUnknown,
215 ArgumentsObjectNotUsed,
216 ArgumentsObjectUsed
217 };
218
219 UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
220
221 ContextType contextType;
222
223 template <typename T>
224 class SmallSet: public QVarLengthArray<T, 8>
225 {
226 public:
227 void insert(int value)
228 {
229 for (auto it : *this) {
230 if (it == value)
231 return;
232 }
233 this->append(value);
234 }
235 };
236
237 // Map from meta property index (existence implies dependency) to notify signal index
238 struct KeyValuePair
239 {
240 quint32 _key = 0;
241 quint32 _value = 0;
242
243 KeyValuePair() {}
244 KeyValuePair(quint32 key, quint32 value): _key(key), _value(value) {}
245
246 quint32 key() const { return _key; }
247 quint32 value() const { return _value; }
248 };
249
250 class PropertyDependencyMap: public QVarLengthArray<KeyValuePair, 8>
251 {
252 public:
253 void insert(quint32 key, quint32 value)
254 {
255 for (auto it = begin(), eit = end(); it != eit; ++it) {
256 if (it->_key == key) {
257 it->_value = value;
258 return;
259 }
260 }
261 append(t: KeyValuePair(key, value));
262 }
263 };
264
265 Context(Context *parent, ContextType type)
266 : parent(parent)
267 , contextType(type)
268 {
269 if (parent && parent->isStrict)
270 isStrict = true;
271 }
272
273 bool hasArgument(const QString &name) const
274 {
275 return arguments.contains(name);
276 }
277
278 int findArgument(const QString &name, bool *isInjected) const
279 {
280 // search backwards to handle duplicate argument names correctly
281 for (int i = arguments.size() - 1; i >= 0; --i) {
282 const auto &arg = arguments.at(i);
283 if (arg.id == name) {
284 *isInjected = arg.isInjected();
285 return i;
286 }
287 }
288 return -1;
289 }
290
291 Member findMember(const QString &name) const
292 {
293 MemberMap::const_iterator it = members.find(key: name);
294 if (it == members.end())
295 return Member();
296 Q_ASSERT(it->index != -1 || !parent);
297 return (*it);
298 }
299
300 bool memberInfo(const QString &name, const Member **m) const
301 {
302 Q_ASSERT(m);
303 MemberMap::const_iterator it = members.find(key: name);
304 if (it == members.end()) {
305 *m = nullptr;
306 return false;
307 }
308 *m = &(*it);
309 return true;
310 }
311
312 bool requiresImplicitReturnValue() const {
313 return contextType == ContextType::Binding ||
314 contextType == ContextType::Eval ||
315 contextType == ContextType::Global || contextType == ContextType::ScriptImportedByQML;
316 }
317
318 void addUsedVariable(const QString &name) {
319 usedVariables.insert(value: name);
320 }
321
322 bool addLocalVar(
323 const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope,
324 QQmlJS::AST::FunctionExpression *function = nullptr,
325 const QQmlJS::SourceLocation &declarationLocation = QQmlJS::SourceLocation(),
326 bool isInjected = false);
327
328 struct ResolvedName {
329 enum Type {
330 Unresolved,
331 QmlGlobal,
332 Global,
333 Local,
334 Stack,
335 Import
336 };
337 Type type = Unresolved;
338 bool isArgOrEval = false;
339 bool isConst = false;
340 bool requiresTDZCheck = false;
341 bool isInjected = false;
342 int scope = -1;
343 int index = -1;
344 QQmlJS::SourceLocation declarationLocation;
345 bool isValid() const { return type != Unresolved; }
346 };
347 ResolvedName resolveName(const QString &name, const QQmlJS::SourceLocation &accessLocation);
348 void emitBlockHeader(Compiler::Codegen *codegen);
349 void emitBlockFooter(Compiler::Codegen *codegen);
350
351 void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
352
353 bool canHaveTailCalls() const
354 {
355 if (!isStrict)
356 return false;
357 if (contextType == ContextType::Function)
358 return !isGenerator;
359 if (contextType == ContextType::Block && parent)
360 return parent->canHaveTailCalls();
361 return false;
362 }
363
364 bool isCaseBlock() const
365 {
366 return contextType == ContextType::Block && name == u"%CaseBlock";
367 }
368};
369
370
371} } // namespace QV4::Compiler
372
373QT_END_NAMESPACE
374
375#endif // QV4CODEGEN_P_H
376

source code of qtdeclarative/src/qml/compiler/qv4compilercontext_p.h