-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCompilationInfo.h
335 lines (271 loc) · 10.3 KB
/
CompilationInfo.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef frontend_CompilationInfo_h
#define frontend_CompilationInfo_h
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Variant.h"
#include "ds/LifoAlloc.h"
#include "frontend/ParserAtom.h"
#include "frontend/SharedContext.h"
#include "frontend/Stencil.h"
#include "frontend/UsedNameTracker.h"
#include "js/GCVariant.h"
#include "js/GCVector.h"
#include "js/HashTable.h"
#include "js/RealmOptions.h"
#include "js/SourceText.h"
#include "js/Vector.h"
#include "js/WasmModule.h"
#include "vm/JSContext.h"
#include "vm/JSFunction.h" // JSFunction
#include "vm/JSScript.h" // SourceExtent
#include "vm/Realm.h"
namespace js {
namespace frontend {
// ScopeContext hold information derivied from the scope and environment chains
// to try to avoid the parser needing to traverse VM structures directly.
struct ScopeContext {
// Whether the enclosing scope allows certain syntax. Eval and arrow scripts
// inherit this from their enclosing scipt so we track it here.
bool allowNewTarget = false;
bool allowSuperProperty = false;
bool allowSuperCall = false;
bool allowArguments = true;
// The type of binding required for `this` of the top level context, as
// indicated by the enclosing scopes of this parse.
ThisBinding thisBinding = ThisBinding::Global;
// Somewhere on the scope chain this parse is embedded within is a 'With'
// scope.
bool inWith = false;
// Somewhere on the scope chain this parse is embedded within a class scope.
bool inClass = false;
// Class field initializer info if we are nested within a class constructor.
// We may be an combination of arrow and eval context within the constructor.
mozilla::Maybe<FieldInitializers> fieldInitializers = {};
explicit ScopeContext(Scope* scope, JSObject* enclosingEnv = nullptr) {
computeAllowSyntax(scope);
computeThisBinding(scope, enclosingEnv);
computeInWith(scope);
computeExternalInitializers(scope);
computeInClass(scope);
}
private:
void computeAllowSyntax(Scope* scope);
void computeThisBinding(Scope* scope, JSObject* environment = nullptr);
void computeInWith(Scope* scope);
void computeExternalInitializers(Scope* scope);
void computeInClass(Scope* scope);
};
struct CompilationInfo;
class ScriptStencilIterable {
public:
class ScriptAndFunction {
public:
ScriptStencil& stencil;
HandleFunction function;
FunctionIndex functionIndex;
ScriptAndFunction() = delete;
ScriptAndFunction(ScriptStencil& stencil, HandleFunction function,
FunctionIndex functionIndex)
: stencil(stencil), function(function), functionIndex(functionIndex) {}
};
class Iterator {
enum class State {
TopLevel,
Functions,
};
State state_ = State::TopLevel;
size_t index_ = 0;
CompilationInfo* compilationInfo_;
Iterator(CompilationInfo* compilationInfo, State state, size_t index)
: state_(state), index_(index), compilationInfo_(compilationInfo) {
skipNonFunctions();
}
public:
explicit Iterator(CompilationInfo* compilationInfo)
: compilationInfo_(compilationInfo) {
skipNonFunctions();
}
Iterator operator++() {
next();
skipNonFunctions();
return *this;
}
inline void next();
inline void skipNonFunctions();
bool operator!=(const Iterator& other) const {
return state_ != other.state_ || index_ != other.index_;
}
inline ScriptAndFunction operator*();
static inline Iterator end(CompilationInfo* compilationInfo);
};
CompilationInfo* compilationInfo_;
explicit ScriptStencilIterable(CompilationInfo* compilationInfo)
: compilationInfo_(compilationInfo) {}
Iterator begin() const { return Iterator(compilationInfo_); }
Iterator end() const { return Iterator::end(compilationInfo_); }
};
// CompilationInfo owns a number of pieces of information about script
// compilation as well as controls the lifetime of parse nodes and other data by
// controling the mark and reset of the LifoAlloc.
struct MOZ_RAII CompilationInfo : public JS::CustomAutoRooter {
static constexpr FunctionIndex TopLevelFunctionIndex = FunctionIndex(0);
JSContext* cx;
const JS::ReadOnlyCompileOptions& options;
// Until we have dealt with Atoms in the front end, we need to hold
// onto them.
AutoKeepAtoms keepAtoms;
// Table of parser atoms for this compilation.
ParserAtomsTable parserAtoms;
Directives directives;
ScopeContext scopeContext;
// List of function contexts for GC tracing. These are allocated in the
// LifoAlloc and still require tracing.
FunctionBox* traceListHead = nullptr;
// The resulting outermost script for the compilation powered
// by this CompilationInfo.
JS::Rooted<JSScript*> script;
JS::Rooted<BaseScript*> lazy;
UsedNameTracker usedNames;
LifoAllocScope& allocScope;
// Hold onto the RegExpCreationData and BigIntCreationData that are allocated
// during parse to ensure correct destruction.
Vector<RegExpCreationData> regExpData;
Vector<BigIntCreationData> bigIntData;
// A Rooted vector to handle tracing of JSFunction*
// and Atoms within.
JS::RootedVector<JSFunction*> functions;
JS::RootedVector<ScriptStencil> funcData;
// The enclosing scope of the function if we're compiling standalone function.
// Null otherwise.
JS::Rooted<Scope*> topLevelFunctionEnclosingScope;
// Stencil for top-level script. This includes standalone functions and
// functions being delazified.
JS::Rooted<ScriptStencil> topLevel;
// A rooted list of scopes created during this parse.
//
// To ensure that ScopeCreationData's destructors fire, and thus our HeapPtr
// barriers, we store the scopeCreationData at this level so that they
// can be safely destroyed, rather than LifoAllocing them with the rest of
// the parser data structures.
//
// References to scopes are controlled via AbstractScopePtr, which holds onto
// an index (and CompilationInfo reference).
JS::RootedVector<ScopeCreationData> scopeCreationData;
// AsmJS modules generated by parsing.
HashMap<FunctionIndex, RefPtr<const JS::WasmModule>> asmJS;
JS::Rooted<ScriptSourceObject*> sourceObject;
// Track the state of key allocations and roll them back as parts of parsing
// get retried. This ensures iteration during stencil instantiation does not
// encounter discarded frontend state.
struct RewindToken {
FunctionBox* funbox = nullptr;
size_t funcDataLength = 0;
};
RewindToken getRewindToken();
void rewind(const RewindToken& pos);
// Construct a CompilationInfo
CompilationInfo(JSContext* cx, LifoAllocScope& alloc,
const JS::ReadOnlyCompileOptions& options,
Scope* enclosingScope = nullptr,
JSObject* enclosingEnv = nullptr)
: JS::CustomAutoRooter(cx),
cx(cx),
options(options),
keepAtoms(cx),
parserAtoms(cx),
directives(options.forceStrictMode()),
scopeContext(enclosingScope, enclosingEnv),
script(cx),
lazy(cx),
usedNames(cx),
allocScope(alloc),
regExpData(cx),
bigIntData(cx),
functions(cx),
funcData(cx),
topLevelFunctionEnclosingScope(cx),
topLevel(cx),
scopeCreationData(cx),
asmJS(cx),
sourceObject(cx) {}
bool init(JSContext* cx);
bool initForStandaloneFunction(JSContext* cx, HandleScope enclosingScope) {
if (!init(cx)) {
return false;
}
this->topLevelFunctionEnclosingScope = enclosingScope;
return true;
}
void initFromLazy(BaseScript* lazy) {
this->lazy = lazy;
this->sourceObject = lazy->sourceObject();
this->topLevelFunctionEnclosingScope = lazy->function()->enclosingScope();
}
void setTopLevelFunctionEnclosingScope(Scope* scope) {
topLevelFunctionEnclosingScope = scope;
}
template <typename Unit>
MOZ_MUST_USE bool assignSource(JS::SourceText<Unit>& sourceBuffer) {
return sourceObject->source()->assignSource(cx, options, sourceBuffer);
}
MOZ_MUST_USE bool instantiateStencils();
void trace(JSTracer* trc) final;
// To avoid any misuses, make sure this is neither copyable,
// movable or assignable.
CompilationInfo(const CompilationInfo&) = delete;
CompilationInfo(CompilationInfo&&) = delete;
CompilationInfo& operator=(const CompilationInfo&) = delete;
CompilationInfo& operator=(CompilationInfo&&) = delete;
ScriptStencilIterable functionScriptStencils() {
return ScriptStencilIterable(this);
}
};
inline void ScriptStencilIterable::Iterator::next() {
if (state_ == State::TopLevel) {
state_ = State::Functions;
} else {
MOZ_ASSERT(index_ < compilationInfo_->funcData.length());
index_++;
}
}
inline void ScriptStencilIterable::Iterator::skipNonFunctions() {
if (state_ == State::TopLevel) {
if (compilationInfo_->topLevel.get().isFunction()) {
return;
}
next();
}
size_t length = compilationInfo_->funcData.length();
while (index_ < length) {
// NOTE: If topLevel is a function, funcData can contain unused item,
// and the item isn't marked as a function.
if (compilationInfo_->funcData[index_].get().isFunction()) {
return;
}
index_++;
}
}
inline ScriptStencilIterable::ScriptAndFunction
ScriptStencilIterable::Iterator::operator*() {
ScriptStencil& stencil = state_ == State::TopLevel
? compilationInfo_->topLevel.get()
: compilationInfo_->funcData[index_].get();
FunctionIndex functionIndex = FunctionIndex(
state_ == State::TopLevel ? CompilationInfo::TopLevelFunctionIndex
: index_);
return ScriptAndFunction(stencil, compilationInfo_->functions[functionIndex],
functionIndex);
}
/* static */ inline ScriptStencilIterable::Iterator
ScriptStencilIterable::Iterator::end(CompilationInfo* compilationInfo) {
return Iterator(compilationInfo, State::Functions,
compilationInfo->funcData.length());
}
} // namespace frontend
} // namespace js
#endif