Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef frontend_ParseContext_h
8
#define frontend_ParseContext_h
9
10
#include "ds/Nestable.h"
11
12
#include "frontend/BytecodeCompiler.h"
13
#include "frontend/ErrorReporter.h"
14
#include "frontend/FunctionTree.h"
15
#include "frontend/NameCollections.h"
16
#include "frontend/SharedContext.h"
17
18
namespace js {
19
20
namespace frontend {
21
22
class ParserBase;
23
24
const char* DeclarationKindString(DeclarationKind kind);
25
26
// Returns true if the declaration is `var` or equivalent.
27
bool DeclarationKindIsVar(DeclarationKind kind);
28
29
bool DeclarationKindIsParameter(DeclarationKind kind);
30
31
// A data structure for tracking used names per parsing session in order to
32
// compute which bindings are closed over. Scripts and scopes are numbered
33
// monotonically in textual order and name uses are tracked by lists of
34
// (script id, scope id) pairs of their use sites.
35
//
36
// Intuitively, in a pair (P,S), P tracks the most nested function that has a
37
// use of u, and S tracks the most nested scope that is still being parsed.
38
//
39
// P is used to answer the question "is u used by a nested function?"
40
// S is used to answer the question "is u used in any scopes currently being
41
// parsed?"
42
//
43
// The algorithm:
44
//
45
// Let Used by a map of names to lists.
46
//
47
// 1. Number all scopes in monotonic increasing order in textual order.
48
// 2. Number all scripts in monotonic increasing order in textual order.
49
// 3. When an identifier u is used in scope numbered S in script numbered P,
50
// and u is found in Used,
51
// a. Append (P,S) to Used[u].
52
// b. Otherwise, assign the the list [(P,S)] to Used[u].
53
// 4. When we finish parsing a scope S in script P, for each declared name d in
54
// Declared(S):
55
// a. If d is found in Used, mark d as closed over if there is a value
56
// (P_d, S_d) in Used[d] such that P_d > P and S_d > S.
57
// b. Remove all values (P_d, S_d) in Used[d] such that S_d are >= S.
58
//
59
// Steps 1 and 2 are implemented by UsedNameTracker::next{Script,Scope}Id.
60
// Step 3 is implemented by UsedNameTracker::noteUsedInScope.
61
// Step 4 is implemented by UsedNameTracker::noteBoundInScope and
62
// Parser::propagateFreeNamesAndMarkClosedOverBindings.
63
class UsedNameTracker {
64
public:
65
struct Use {
66
uint32_t scriptId;
67
uint32_t scopeId;
68
};
69
70
class UsedNameInfo {
71
friend class UsedNameTracker;
72
73
Vector<Use, 6> uses_;
74
75
void resetToScope(uint32_t scriptId, uint32_t scopeId);
76
77
public:
78
explicit UsedNameInfo(JSContext* cx) : uses_(cx) {}
79
80
UsedNameInfo(UsedNameInfo&& other) : uses_(std::move(other.uses_)) {}
81
82
bool noteUsedInScope(uint32_t scriptId, uint32_t scopeId) {
83
if (uses_.empty() || uses_.back().scopeId < scopeId) {
84
return uses_.append(Use{scriptId, scopeId});
85
}
86
return true;
87
}
88
89
void noteBoundInScope(uint32_t scriptId, uint32_t scopeId,
90
bool* closedOver) {
91
*closedOver = false;
92
while (!uses_.empty()) {
93
Use& innermost = uses_.back();
94
if (innermost.scopeId < scopeId) {
95
break;
96
}
97
if (innermost.scriptId > scriptId) {
98
*closedOver = true;
99
}
100
uses_.popBack();
101
}
102
}
103
104
bool isUsedInScript(uint32_t scriptId) const {
105
return !uses_.empty() && uses_.back().scriptId >= scriptId;
106
}
107
};
108
109
using UsedNameMap = HashMap<JSAtom*, UsedNameInfo, DefaultHasher<JSAtom*>>;
110
111
private:
112
// The map of names to chains of uses.
113
UsedNameMap map_;
114
115
// Monotonically increasing id for all nested scripts.
116
uint32_t scriptCounter_;
117
118
// Monotonically increasing id for all nested scopes.
119
uint32_t scopeCounter_;
120
121
public:
122
explicit UsedNameTracker(JSContext* cx)
123
: map_(cx), scriptCounter_(0), scopeCounter_(0) {}
124
125
uint32_t nextScriptId() {
126
MOZ_ASSERT(scriptCounter_ != UINT32_MAX,
127
"ParseContext::Scope::init should have prevented wraparound");
128
return scriptCounter_++;
129
}
130
131
uint32_t nextScopeId() {
132
MOZ_ASSERT(scopeCounter_ != UINT32_MAX);
133
return scopeCounter_++;
134
}
135
136
UsedNameMap::Ptr lookup(JSAtom* name) const { return map_.lookup(name); }
137
138
MOZ_MUST_USE bool noteUse(JSContext* cx, JSAtom* name, uint32_t scriptId,
139
uint32_t scopeId);
140
141
MOZ_MUST_USE bool markAsAlwaysClosedOver(JSContext* cx, JSAtom* name,
142
uint32_t scriptId,
143
uint32_t scopeId) {
144
// This marks a variable as always closed over:
145
// UsedNameInfo::noteBoundInScope only checks if scriptId and scopeId are
146
// greater than the current scriptId/scopeId, so do a simple increment to
147
// make that so.
148
return noteUse(cx, name, scriptId + 1, scopeId + 1);
149
}
150
151
struct RewindToken {
152
private:
153
friend class UsedNameTracker;
154
uint32_t scriptId;
155
uint32_t scopeId;
156
};
157
158
RewindToken getRewindToken() const {
159
RewindToken token;
160
token.scriptId = scriptCounter_;
161
token.scopeId = scopeCounter_;
162
return token;
163
}
164
165
// Resets state so that scriptId and scopeId are the innermost script and
166
// scope, respectively. Used for rewinding state on syntax parse failure.
167
void rewind(RewindToken token);
168
169
// Resets state to beginning of compilation.
170
void reset() {
171
map_.clear();
172
RewindToken token;
173
token.scriptId = 0;
174
token.scopeId = 0;
175
rewind(token);
176
}
177
};
178
179
class FunctionTree;
180
class FunctionTreeHolder;
181
182
/*
183
* The struct ParseContext stores information about the current parsing context,
184
* which is part of the parser state (see the field Parser::pc). The current
185
* parsing context is either the global context, or the function currently being
186
* parsed. When the parser encounters a function definition, it creates a new
187
* ParseContext, makes it the new current context.
188
*/
189
class ParseContext : public Nestable<ParseContext> {
190
public:
191
// The intra-function statement stack.
192
//
193
// Used for early error checking that depend on the nesting structure of
194
// statements, such as continue/break targets, labels, and unbraced
195
// lexical declarations.
196
class Statement : public Nestable<Statement> {
197
StatementKind kind_;
198
199
public:
200
using Nestable<Statement>::enclosing;
201
using Nestable<Statement>::findNearest;
202
203
Statement(ParseContext* pc, StatementKind kind)
204
: Nestable<Statement>(&pc->innermostStatement_), kind_(kind) {}
205
206
template <typename T>
207
inline bool is() const;
208
template <typename T>
209
inline T& as();
210
211
StatementKind kind() const { return kind_; }
212
213
void refineForKind(StatementKind newForKind) {
214
MOZ_ASSERT(kind_ == StatementKind::ForLoop);
215
MOZ_ASSERT(newForKind == StatementKind::ForInLoop ||
216
newForKind == StatementKind::ForOfLoop);
217
kind_ = newForKind;
218
}
219
};
220
221
class LabelStatement : public Statement {
222
RootedAtom label_;
223
224
public:
225
LabelStatement(ParseContext* pc, JSAtom* label)
226
: Statement(pc, StatementKind::Label), label_(pc->sc_->cx_, label) {}
227
228
HandleAtom label() const { return label_; }
229
};
230
231
struct ClassStatement : public Statement {
232
FunctionBox* constructorBox;
233
234
explicit ClassStatement(ParseContext* pc)
235
: Statement(pc, StatementKind::Class), constructorBox(nullptr) {}
236
};
237
238
// The intra-function scope stack.
239
//
240
// Tracks declared and used names within a scope.
241
class Scope : public Nestable<Scope> {
242
// Names declared in this scope. Corresponds to the union of
243
// VarDeclaredNames and LexicallyDeclaredNames in the ES spec.
244
//
245
// A 'var' declared name is a member of the declared name set of every
246
// scope in its scope contour.
247
//
248
// A lexically declared name is a member only of the declared name set of
249
// the scope in which it is declared.
250
PooledMapPtr<DeclaredNameMap> declared_;
251
252
// FunctionBoxes in this scope that need to be considered for Annex
253
// B.3.3 semantics. This is checked on Scope exit, as by then we have
254
// all the declared names and would know if Annex B.3.3 is applicable.
255
using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>;
256
PooledVectorPtr<FunctionBoxVector> possibleAnnexBFunctionBoxes_;
257
258
// Monotonically increasing id.
259
uint32_t id_;
260
261
bool maybeReportOOM(ParseContext* pc, bool result) {
262
if (!result) {
263
ReportOutOfMemory(pc->sc()->cx_);
264
}
265
return result;
266
}
267
268
public:
269
using DeclaredNamePtr = DeclaredNameMap::Ptr;
270
using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
271
272
using Nestable<Scope>::enclosing;
273
274
explicit inline Scope(ParserBase* parser);
275
explicit inline Scope(JSContext* cx, ParseContext* pc,
276
UsedNameTracker& usedNames);
277
278
void dump(ParseContext* pc);
279
280
uint32_t id() const { return id_; }
281
282
MOZ_MUST_USE bool init(ParseContext* pc) {
283
if (id_ == UINT32_MAX) {
284
pc->errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
285
return false;
286
}
287
288
return declared_.acquire(pc->sc()->cx_);
289
}
290
291
bool isEmpty() const { return declared_->all().empty(); }
292
293
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
294
return declared_->lookup(name);
295
}
296
297
AddDeclaredNamePtr lookupDeclaredNameForAdd(JSAtom* name) {
298
return declared_->lookupForAdd(name);
299
}
300
301
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p,
302
JSAtom* name, DeclarationKind kind,
303
uint32_t pos) {
304
return maybeReportOOM(
305
pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
306
}
307
308
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
309
MOZ_MUST_USE bool addPossibleAnnexBFunctionBox(ParseContext* pc,
310
FunctionBox* funbox);
311
312
// Check if the candidate function boxes for Annex B.3.3 should in
313
// fact get Annex B semantics. Checked on Scope exit.
314
MOZ_MUST_USE bool propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc);
315
316
// Add and remove catch parameter names. Used to implement the odd
317
// semantics of catch bodies.
318
bool addCatchParameters(ParseContext* pc, Scope& catchParamScope);
319
void removeCatchParameters(ParseContext* pc, Scope& catchParamScope);
320
321
void useAsVarScope(ParseContext* pc) {
322
MOZ_ASSERT(!pc->varScope_);
323
pc->varScope_ = this;
324
}
325
326
// An iterator for the set of names a scope binds: the set of all
327
// declared names for 'var' scopes, and the set of lexically declared
328
// names for non-'var' scopes.
329
class BindingIter {
330
friend class Scope;
331
332
DeclaredNameMap::Range declaredRange_;
333
mozilla::DebugOnly<uint32_t> count_;
334
bool isVarScope_;
335
336
BindingIter(Scope& scope, bool isVarScope)
337
: declaredRange_(scope.declared_->all()),
338
count_(0),
339
isVarScope_(isVarScope) {
340
settle();
341
}
342
343
void settle() {
344
// Both var and lexically declared names are binding in a var
345
// scope.
346
if (isVarScope_) {
347
return;
348
}
349
350
// Otherwise, pop only lexically declared names are
351
// binding. Pop the range until we find such a name.
352
while (!declaredRange_.empty()) {
353
if (BindingKindIsLexical(kind())) {
354
break;
355
}
356
declaredRange_.popFront();
357
}
358
}
359
360
public:
361
bool done() const { return declaredRange_.empty(); }
362
363
explicit operator bool() const { return !done(); }
364
365
JSAtom* name() {
366
MOZ_ASSERT(!done());
367
return declaredRange_.front().key();
368
}
369
370
DeclarationKind declarationKind() {
371
MOZ_ASSERT(!done());
372
return declaredRange_.front().value()->kind();
373
}
374
375
BindingKind kind() {
376
return DeclarationKindToBindingKind(declarationKind());
377
}
378
379
bool closedOver() {
380
MOZ_ASSERT(!done());
381
return declaredRange_.front().value()->closedOver();
382
}
383
384
void setClosedOver() {
385
MOZ_ASSERT(!done());
386
return declaredRange_.front().value()->setClosedOver();
387
}
388
389
void operator++(int) {
390
MOZ_ASSERT(!done());
391
MOZ_ASSERT(count_ != UINT32_MAX);
392
declaredRange_.popFront();
393
settle();
394
}
395
};
396
397
inline BindingIter bindings(ParseContext* pc);
398
};
399
400
class VarScope : public Scope {
401
public:
402
explicit inline VarScope(ParserBase* parser);
403
explicit inline VarScope(JSContext* cx, ParseContext* pc,
404
UsedNameTracker& usedNames);
405
};
406
407
private:
408
// Not all contexts are Function contexts, hence the maybe
409
mozilla::Maybe<AutoPushTree> tree;
410
411
// Trace logging of parsing time.
412
AutoFrontendTraceLog traceLog_;
413
414
// Context shared between parsing and bytecode generation.
415
SharedContext* sc_;
416
417
// A mechanism used for error reporting.
418
ErrorReporter& errorReporter_;
419
420
// The innermost statement, i.e., top of the statement stack.
421
Statement* innermostStatement_;
422
423
// The innermost scope, i.e., top of the scope stack.
424
//
425
// The outermost scope in the stack is usually varScope_. In the case of
426
// functions, the outermost scope is functionScope_, which may be
427
// varScope_. See comment above functionScope_.
428
Scope* innermostScope_;
429
430
// If isFunctionBox() and the function is a named lambda, the DeclEnv
431
// scope for named lambdas.
432
mozilla::Maybe<Scope> namedLambdaScope_;
433
434
// If isFunctionBox(), the scope for the function. If there are no
435
// parameter expressions, this is scope for the entire function. If there
436
// are parameter expressions, this holds the special function names
437
// ('.this', 'arguments') and the formal parameters.
438
mozilla::Maybe<Scope> functionScope_;
439
440
// The body-level scope. This always exists, but not necessarily at the
441
// beginning of parsing the script in the case of functions with parameter
442
// expressions.
443
Scope* varScope_;
444
445
// Simple formal parameter names, in order of appearance. Only used when
446
// isFunctionBox().
447
PooledVectorPtr<AtomVector> positionalFormalParameterNames_;
448
449
// Closed over binding names, in order of appearance. Null-delimited
450
// between scopes. Only used when syntax parsing.
451
PooledVectorPtr<AtomVector> closedOverBindingsForLazy_;
452
453
public:
454
// All inner FunctionBoxes in this context. Only used when syntax parsing.
455
// The FunctionBoxes are traced as part of the TraceList on the parser,
456
// (see TraceListNode::TraceList)
457
FunctionBoxVector innerFunctionBoxesForLazy;
458
459
// In a function context, points to a Directive struct that can be updated
460
// to reflect new directives encountered in the Directive Prologue that
461
// require reparsing the function. In global/module/generator-tail contexts,
462
// we don't need to reparse when encountering a DirectivePrologue so this
463
// pointer may be nullptr.
464
Directives* newDirectives;
465
466
// lastYieldOffset stores the offset of the last yield that was parsed.
467
// NoYieldOffset is its initial value.
468
static const uint32_t NoYieldOffset = UINT32_MAX;
469
uint32_t lastYieldOffset;
470
471
// lastAwaitOffset stores the offset of the last await that was parsed.
472
// NoAwaitOffset is its initial value.
473
static const uint32_t NoAwaitOffset = UINT32_MAX;
474
uint32_t lastAwaitOffset;
475
476
private:
477
// Monotonically increasing id.
478
uint32_t scriptId_;
479
480
// Set when compiling a function using Parser::standaloneFunctionBody via
481
// the Function or Generator constructor.
482
bool isStandaloneFunctionBody_;
483
484
// Set when encountering a super.property inside a method. We need to mark
485
// the nearest super scope as needing a home object.
486
bool superScopeNeedsHomeObject_;
487
488
public:
489
ParseContext(JSContext* cx, ParseContext*& parent, SharedContext* sc,
490
ErrorReporter& errorReporter, UsedNameTracker& usedNames,
491
FunctionTreeHolder& treeHolder, Directives* newDirectives,
492
bool isFull);
493
494
MOZ_MUST_USE bool init();
495
496
SharedContext* sc() { return sc_; }
497
498
// `true` if we are in the body of a function definition.
499
bool isFunctionBox() const { return sc_->isFunctionBox(); }
500
501
FunctionBox* functionBox() { return sc_->asFunctionBox(); }
502
503
Statement* innermostStatement() { return innermostStatement_; }
504
505
Scope* innermostScope() {
506
// There is always at least one scope: the 'var' scope.
507
MOZ_ASSERT(innermostScope_);
508
return innermostScope_;
509
}
510
511
Scope& namedLambdaScope() {
512
MOZ_ASSERT(functionBox()->isNamedLambda());
513
return *namedLambdaScope_;
514
}
515
516
Scope& functionScope() {
517
MOZ_ASSERT(isFunctionBox());
518
return *functionScope_;
519
}
520
521
Scope& varScope() {
522
MOZ_ASSERT(varScope_);
523
return *varScope_;
524
}
525
526
bool isFunctionExtraBodyVarScopeInnermost() {
527
return isFunctionBox() && functionBox()->hasParameterExprs &&
528
innermostScope() == varScope_;
529
}
530
531
template <typename Predicate /* (Statement*) -> bool */>
532
Statement* findInnermostStatement(Predicate predicate) {
533
return Statement::findNearest(innermostStatement_, predicate);
534
}
535
536
template <typename T, typename Predicate /* (Statement*) -> bool */>
537
T* findInnermostStatement(Predicate predicate) {
538
return Statement::findNearest<T>(innermostStatement_, predicate);
539
}
540
541
template <typename T>
542
T* findInnermostStatement() {
543
return Statement::findNearest<T>(innermostStatement_);
544
}
545
546
AtomVector& positionalFormalParameterNames() {
547
return *positionalFormalParameterNames_;
548
}
549
550
AtomVector& closedOverBindingsForLazy() {
551
return *closedOverBindingsForLazy_;
552
}
553
554
enum class BreakStatementError {
555
// Unlabeled break must be inside loop or switch.
556
ToughBreak,
557
LabelNotFound,
558
};
559
560
// Return Err(true) if we have encountered at least one loop,
561
// Err(false) otherwise.
562
MOZ_MUST_USE inline JS::Result<Ok, BreakStatementError> checkBreakStatement(
563
PropertyName* label);
564
565
enum class ContinueStatementError {
566
NotInALoop,
567
LabelNotFound,
568
};
569
MOZ_MUST_USE inline JS::Result<Ok, ContinueStatementError>
570
checkContinueStatement(PropertyName* label);
571
572
// True if we are at the topmost level of a entire script or function body.
573
// For example, while parsing this code we would encounter f1 and f2 at
574
// body level, but we would not encounter f3 or f4 at body level:
575
//
576
// function f1() { function f2() { } }
577
// if (cond) { function f3() { if (cond) { function f4() { } } } }
578
//
579
bool atBodyLevel() { return !innermostStatement_; }
580
581
bool atGlobalLevel() { return atBodyLevel() && sc_->isGlobalContext(); }
582
583
// True if we are at the topmost level of a module only.
584
bool atModuleLevel() { return atBodyLevel() && sc_->isModuleContext(); }
585
586
// True if we are at the topmost level of an entire script or module. For
587
// example, in the comment on |atBodyLevel()| above, we would encounter |f1|
588
// and the outermost |if (cond)| at top level, and everything else would not
589
// be at top level.
590
bool atTopLevel() { return atBodyLevel() && sc_->isTopLevelContext(); }
591
592
void setIsStandaloneFunctionBody() { isStandaloneFunctionBody_ = true; }
593
594
bool isStandaloneFunctionBody() const { return isStandaloneFunctionBody_; }
595
596
void setSuperScopeNeedsHomeObject() {
597
MOZ_ASSERT(sc_->allowSuperProperty());
598
superScopeNeedsHomeObject_ = true;
599
}
600
601
bool superScopeNeedsHomeObject() const { return superScopeNeedsHomeObject_; }
602
603
bool useAsmOrInsideUseAsm() const {
604
return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
605
}
606
607
// A generator is marked as a generator before its body is parsed.
608
GeneratorKind generatorKind() const {
609
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind()
610
: GeneratorKind::NotGenerator;
611
}
612
613
bool isGenerator() const {
614
return generatorKind() == GeneratorKind::Generator;
615
}
616
617
bool isAsync() const {
618
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
619
}
620
621
bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
622
623
FunctionAsyncKind asyncKind() const {
624
return isAsync() ? FunctionAsyncKind::AsyncFunction
625
: FunctionAsyncKind::SyncFunction;
626
}
627
628
bool isArrowFunction() const {
629
return sc_->isFunctionBox() && sc_->asFunctionBox()->isArrow();
630
}
631
632
bool isMethod() const {
633
return sc_->isFunctionBox() && sc_->asFunctionBox()->isMethod();
634
}
635
636
bool isGetterOrSetter() const {
637
return sc_->isFunctionBox() && (sc_->asFunctionBox()->isGetter() ||
638
sc_->asFunctionBox()->isSetter());
639
}
640
641
uint32_t scriptId() const { return scriptId_; }
642
643
bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
644
645
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
646
uint32_t beginPos,
647
mozilla::Maybe<DeclarationKind>* redeclaredKind,
648
uint32_t* prevPos);
649
650
bool hasUsedName(const UsedNameTracker& usedNames, HandlePropertyName name);
651
bool hasUsedFunctionSpecialName(const UsedNameTracker& usedNames,
652
HandlePropertyName name);
653
654
bool declareFunctionThis(const UsedNameTracker& usedNames,
655
bool canSkipLazyClosedOverBindings);
656
bool declareFunctionArgumentsObject(const UsedNameTracker& usedNames,
657
bool canSkipLazyClosedOverBindings);
658
bool declareDotGeneratorName();
659
660
private:
661
mozilla::Maybe<DeclarationKind> isVarRedeclaredInInnermostScope(
662
HandlePropertyName name, DeclarationKind kind);
663
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
664
DeclarationKind kind);
665
666
enum DryRunOption { NotDryRun, DryRunInnermostScopeOnly };
667
template <DryRunOption dryRunOption>
668
bool tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind,
669
uint32_t beginPos,
670
mozilla::Maybe<DeclarationKind>* redeclaredKind,
671
uint32_t* prevPos);
672
};
673
674
} // namespace frontend
675
676
} // namespace js
677
678
#endif // frontend_ParseContext_h