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