Bug 1377272 - Making ParserContext less intertwined with Parser;r=shu+381259

To implement the BinJS decoder of Bug 1377007, we need to access ParserContext without a Parser. This patch makes it possible to construct one.

MozReview-Commit-ID: Fx3S0UkU7Hq

--HG--
extra : rebase_source : 7470b03fb45bd8d80ffd66b1507d1491b5e3158f
This commit is contained in:
David Teller 2017-08-03 11:27:03 +02:00
parent 436186d6f8
commit 164c1cbbcd
9 changed files with 794 additions and 701 deletions

View File

@ -14,6 +14,7 @@
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/ErrorReporter.h"
#include "frontend/FoldConstants.h"
#include "frontend/NameFunctions.h"
#include "frontend/Parser.h"
@ -107,21 +108,21 @@ AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextI
#endif
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream)
const ErrorReporter& errorReporter)
#ifdef JS_TRACE_LOGGING
: logger_(TraceLoggerForCurrentThread(cx))
{
// If the tokenizer hasn't yet gotten any tokens, use the line and column
// numbers from CompileOptions.
uint32_t line, column;
if (tokenStream.isCurrentTokenType(TOK_EOF) && !tokenStream.isEOF()) {
line = tokenStream.options().lineno;
column = tokenStream.options().column;
if (errorReporter.hasTokenizationStarted()) {
line = errorReporter.options().lineno;
column = errorReporter.options().column;
} else {
uint32_t offset = tokenStream.currentToken().pos.begin;
tokenStream.srcCoords.lineNumAndColumnIndex(offset, &line, &column);
uint32_t offset = errorReporter.offset();
errorReporter.lineNumAndColumnIndex(offset, &line, &column);
}
frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column);
frontendLog_.emplace(logger_, *frontendEvent_);
typeLog_.emplace(logger_, id);
}
@ -130,12 +131,12 @@ AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextI
#endif
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream,
const ErrorReporter& errorReporter,
FunctionBox* funbox)
#ifdef JS_TRACE_LOGGING
: logger_(TraceLoggerForCurrentThread(cx))
{
frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(),
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(),
funbox->startLine, funbox->startColumn);
frontendLog_.emplace(logger_, *frontendEvent_);
typeLog_.emplace(logger_, id);
@ -145,13 +146,13 @@ AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextI
#endif
AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream, ParseNode* pn)
const ErrorReporter& errorReporter, ParseNode* pn)
#ifdef JS_TRACE_LOGGING
: logger_(TraceLoggerForCurrentThread(cx))
{
uint32_t line, column;
tokenStream.srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
errorReporter.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column);
frontendLog_.emplace(logger_, *frontendEvent_);
typeLog_.emplace(logger_, id);
}

View File

@ -26,7 +26,7 @@ class ScriptSourceObject;
namespace frontend {
class TokenStreamAnyChars;
class ErrorReporter;
class FunctionBox;
class ParseNode;
@ -143,13 +143,13 @@ class MOZ_STACK_CLASS AutoFrontendTraceLog
const char* filename, size_t line, size_t column);
AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream);
const ErrorReporter& reporter);
AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream, FunctionBox* funbox);
const ErrorReporter& reporter, FunctionBox* funbox);
AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
const TokenStreamAnyChars& tokenStream, ParseNode* pn);
const ErrorReporter& reporter, ParseNode* pn);
};
} /* namespace frontend */

View File

@ -0,0 +1,21 @@
#ifndef frontend_ErrorReporter_h
#define frontend_ErrorReporter_h
namespace js {
namespace frontend {
class ErrorReporter
{
public:
virtual const ReadOnlyCompileOptions& options() const = 0;
virtual void lineNumAndColumnIndex(size_t offset, uint32_t* line, uint32_t* column) const = 0;
virtual size_t offset() const = 0;
virtual bool hasTokenizationStarted() const = 0;
virtual void reportErrorNoOffset(unsigned errorNumber, ...) = 0;
virtual const char* getFilename() const = 0;
};
} // namespace frontend
} // namespace js
#endif // frontend_ErrorReporter_h

View File

@ -0,0 +1,689 @@
#ifndef frontend_ParseContext_h
#define frontend_ParseContext_h
#include "frontend/ErrorReporter.h"
namespace js {
namespace frontend {
class ParserBase;
// A data structure for tracking used names per parsing session in order to
// compute which bindings are closed over. Scripts and scopes are numbered
// monotonically in textual order and name uses are tracked by lists of
// (script id, scope id) pairs of their use sites.
//
// Intuitively, in a pair (P,S), P tracks the most nested function that has a
// use of u, and S tracks the most nested scope that is still being parsed.
//
// P is used to answer the question "is u used by a nested function?"
// S is used to answer the question "is u used in any scopes currently being
// parsed?"
//
// The algorithm:
//
// Let Used by a map of names to lists.
//
// 1. Number all scopes in monotonic increasing order in textual order.
// 2. Number all scripts in monotonic increasing order in textual order.
// 3. When an identifier u is used in scope numbered S in script numbered P,
// and u is found in Used,
// a. Append (P,S) to Used[u].
// b. Otherwise, assign the the list [(P,S)] to Used[u].
// 4. When we finish parsing a scope S in script P, for each declared name d in
// Declared(S):
// a. If d is found in Used, mark d as closed over if there is a value
// (P_d, S_d) in Used[d] such that P_d > P and S_d > S.
// b. Remove all values (P_d, S_d) in Used[d] such that S_d are >= S.
//
// Steps 1 and 2 are implemented by UsedNameTracker::next{Script,Scope}Id.
// Step 3 is implemented by UsedNameTracker::noteUsedInScope.
// Step 4 is implemented by UsedNameTracker::noteBoundInScope and
// Parser::propagateFreeNamesAndMarkClosedOverBindings.
class UsedNameTracker
{
public:
struct Use
{
uint32_t scriptId;
uint32_t scopeId;
};
class UsedNameInfo
{
friend class UsedNameTracker;
Vector<Use, 6> uses_;
void resetToScope(uint32_t scriptId, uint32_t scopeId);
public:
explicit UsedNameInfo(JSContext* cx)
: uses_(cx)
{ }
UsedNameInfo(UsedNameInfo&& other)
: uses_(mozilla::Move(other.uses_))
{ }
bool noteUsedInScope(uint32_t scriptId, uint32_t scopeId) {
if (uses_.empty() || uses_.back().scopeId < scopeId)
return uses_.append(Use { scriptId, scopeId });
return true;
}
void noteBoundInScope(uint32_t scriptId, uint32_t scopeId, bool* closedOver) {
*closedOver = false;
while (!uses_.empty()) {
Use& innermost = uses_.back();
if (innermost.scopeId < scopeId)
break;
if (innermost.scriptId > scriptId)
*closedOver = true;
uses_.popBack();
}
}
bool isUsedInScript(uint32_t scriptId) const {
return !uses_.empty() && uses_.back().scriptId >= scriptId;
}
};
using UsedNameMap = HashMap<JSAtom*,
UsedNameInfo,
DefaultHasher<JSAtom*>>;
private:
// The map of names to chains of uses.
UsedNameMap map_;
// Monotonically increasing id for all nested scripts.
uint32_t scriptCounter_;
// Monotonically increasing id for all nested scopes.
uint32_t scopeCounter_;
public:
explicit UsedNameTracker(JSContext* cx)
: map_(cx),
scriptCounter_(0),
scopeCounter_(0)
{ }
MOZ_MUST_USE bool init() {
return map_.init();
}
uint32_t nextScriptId() {
MOZ_ASSERT(scriptCounter_ != UINT32_MAX,
"ParseContext::Scope::init should have prevented wraparound");
return scriptCounter_++;
}
uint32_t nextScopeId() {
MOZ_ASSERT(scopeCounter_ != UINT32_MAX);
return scopeCounter_++;
}
UsedNameMap::Ptr lookup(JSAtom* name) const {
return map_.lookup(name);
}
MOZ_MUST_USE bool noteUse(JSContext* cx, JSAtom* name,
uint32_t scriptId, uint32_t scopeId);
struct RewindToken
{
private:
friend class UsedNameTracker;
uint32_t scriptId;
uint32_t scopeId;
};
RewindToken getRewindToken() const {
RewindToken token;
token.scriptId = scriptCounter_;
token.scopeId = scopeCounter_;
return token;
}
// Resets state so that scriptId and scopeId are the innermost script and
// scope, respectively. Used for rewinding state on syntax parse failure.
void rewind(RewindToken token);
// Resets state to beginning of compilation.
void reset() {
map_.clear();
RewindToken token;
token.scriptId = 0;
token.scopeId = 0;
rewind(token);
}
};
/*
* The struct ParseContext stores information about the current parsing context,
* which is part of the parser state (see the field Parser::pc). The current
* parsing context is either the global context, or the function currently being
* parsed. When the parser encounters a function definition, it creates a new
* ParseContext, makes it the new current context.
*/
class ParseContext : public Nestable<ParseContext>
{
public:
// The intra-function statement stack.
//
// Used for early error checking that depend on the nesting structure of
// statements, such as continue/break targets, labels, and unbraced
// lexical declarations.
class Statement : public Nestable<Statement>
{
StatementKind kind_;
public:
using Nestable<Statement>::enclosing;
using Nestable<Statement>::findNearest;
Statement(ParseContext* pc, StatementKind kind)
: Nestable<Statement>(&pc->innermostStatement_),
kind_(kind)
{ }
template <typename T> inline bool is() const;
template <typename T> inline T& as();
StatementKind kind() const {
return kind_;
}
void refineForKind(StatementKind newForKind) {
MOZ_ASSERT(kind_ == StatementKind::ForLoop);
MOZ_ASSERT(newForKind == StatementKind::ForInLoop ||
newForKind == StatementKind::ForOfLoop);
kind_ = newForKind;
}
};
class LabelStatement : public Statement
{
RootedAtom label_;
public:
LabelStatement(ParseContext* pc, JSAtom* label)
: Statement(pc, StatementKind::Label),
label_(pc->sc_->context, label)
{ }
HandleAtom label() const {
return label_;
}
};
struct ClassStatement : public Statement
{
FunctionBox* constructorBox;
explicit ClassStatement(ParseContext* pc)
: Statement(pc, StatementKind::Class),
constructorBox(nullptr)
{ }
};
// The intra-function scope stack.
//
// Tracks declared and used names within a scope.
class Scope : public Nestable<Scope>
{
// Names declared in this scope. Corresponds to the union of
// VarDeclaredNames and LexicallyDeclaredNames in the ES spec.
//
// A 'var' declared name is a member of the declared name set of every
// scope in its scope contour.
//
// A lexically declared name is a member only of the declared name set of
// the scope in which it is declared.
PooledMapPtr<DeclaredNameMap> declared_;
// FunctionBoxes in this scope that need to be considered for Annex
// B.3.3 semantics. This is checked on Scope exit, as by then we have
// all the declared names and would know if Annex B.3.3 is applicable.
PooledVectorPtr<FunctionBoxVector> possibleAnnexBFunctionBoxes_;
// Monotonically increasing id.
uint32_t id_;
bool maybeReportOOM(ParseContext* pc, bool result) {
if (!result)
ReportOutOfMemory(pc->sc()->context);
return result;
}
public:
using DeclaredNamePtr = DeclaredNameMap::Ptr;
using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
using Nestable<Scope>::enclosing;
explicit inline Scope(ParserBase* parser);
explicit inline Scope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames);
void dump(ParseContext* pc);
uint32_t id() const {
return id_;
}
MOZ_MUST_USE bool init(ParseContext* pc) {
if (id_ == UINT32_MAX) {
pc->errorReporter_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
}
return declared_.acquire(pc->sc()->context);
}
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
return declared_->lookup(name);
}
AddDeclaredNamePtr lookupDeclaredNameForAdd(JSAtom* name) {
return declared_->lookupForAdd(name);
}
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name,
DeclarationKind kind, uint32_t pos)
{
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
MOZ_MUST_USE bool addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox);
// Check if the candidate function boxes for Annex B.3.3 should in
// fact get Annex B semantics. Checked on Scope exit.
MOZ_MUST_USE bool propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc);
// Add and remove catch parameter names. Used to implement the odd
// semantics of catch bodies.
bool addCatchParameters(ParseContext* pc, Scope& catchParamScope);
void removeCatchParameters(ParseContext* pc, Scope& catchParamScope);
void useAsVarScope(ParseContext* pc) {
MOZ_ASSERT(!pc->varScope_);
pc->varScope_ = this;
}
// An iterator for the set of names a scope binds: the set of all
// declared names for 'var' scopes, and the set of lexically declared
// names for non-'var' scopes.
class BindingIter
{
friend class Scope;
DeclaredNameMap::Range declaredRange_;
mozilla::DebugOnly<uint32_t> count_;
bool isVarScope_;
BindingIter(Scope& scope, bool isVarScope)
: declaredRange_(scope.declared_->all()),
count_(0),
isVarScope_(isVarScope)
{
settle();
}
void settle() {
// Both var and lexically declared names are binding in a var
// scope.
if (isVarScope_)
return;
// Otherwise, pop only lexically declared names are
// binding. Pop the range until we find such a name.
while (!declaredRange_.empty()) {
if (BindingKindIsLexical(kind()))
break;
declaredRange_.popFront();
}
}
public:
bool done() const {
return declaredRange_.empty();
}
explicit operator bool() const {
return !done();
}
JSAtom* name() {
MOZ_ASSERT(!done());
return declaredRange_.front().key();
}
DeclarationKind declarationKind() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->kind();
}
BindingKind kind() {
return DeclarationKindToBindingKind(declarationKind());
}
bool closedOver() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->closedOver();
}
void setClosedOver() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->setClosedOver();
}
void operator++(int) {
MOZ_ASSERT(!done());
MOZ_ASSERT(count_ != UINT32_MAX);
declaredRange_.popFront();
settle();
}
};
inline BindingIter bindings(ParseContext* pc);
};
class VarScope : public Scope
{
public:
explicit inline VarScope(ParserBase* parser);
};
private:
// Trace logging of parsing time.
AutoFrontendTraceLog traceLog_;
// Context shared between parsing and bytecode generation.
SharedContext* sc_;
// A mechanism used for error reporting.
ErrorReporter& errorReporter_;
// The innermost statement, i.e., top of the statement stack.
Statement* innermostStatement_;
// The innermost scope, i.e., top of the scope stack.
//
// The outermost scope in the stack is usually varScope_. In the case of
// functions, the outermost scope is functionScope_, which may be
// varScope_. See comment above functionScope_.
Scope* innermostScope_;
// If isFunctionBox() and the function is a named lambda, the DeclEnv
// scope for named lambdas.
mozilla::Maybe<Scope> namedLambdaScope_;
// If isFunctionBox(), the scope for the function. If there are no
// parameter expressions, this is scope for the entire function. If there
// are parameter expressions, this holds the special function names
// ('.this', 'arguments') and the formal parameters.
mozilla::Maybe<Scope> functionScope_;
// The body-level scope. This always exists, but not necessarily at the
// beginning of parsing the script in the case of functions with parameter
// expressions.
Scope* varScope_;
// Simple formal parameter names, in order of appearance. Only used when
// isFunctionBox().
PooledVectorPtr<AtomVector> positionalFormalParameterNames_;
// Closed over binding names, in order of appearance. Null-delimited
// between scopes. Only used when syntax parsing.
PooledVectorPtr<AtomVector> closedOverBindingsForLazy_;
// Monotonically increasing id.
uint32_t scriptId_;
// Set when compiling a function using Parser::standaloneFunctionBody via
// the Function or Generator constructor.
bool isStandaloneFunctionBody_;
// Set when encountering a super.property inside a method. We need to mark
// the nearest super scope as needing a home object.
bool superScopeNeedsHomeObject_;
public:
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
// lastAwaitOffset stores the offset of the last await that was parsed.
// NoAwaitOffset is its initial value.
static const uint32_t NoAwaitOffset = UINT32_MAX;
uint32_t lastAwaitOffset;
// All inner functions in this context. Only used when syntax parsing.
Rooted<GCVector<JSFunction*, 8>> innerFunctionsForLazy;
// In a function context, points to a Directive struct that can be updated
// to reflect new directives encountered in the Directive Prologue that
// require reparsing the function. In global/module/generator-tail contexts,
// we don't need to reparse when encountering a DirectivePrologue so this
// pointer may be nullptr.
Directives* newDirectives;
// Set when parsing a function and it has 'return <expr>;'
bool funHasReturnExpr;
// Set when parsing a function and it has 'return;'
bool funHasReturnVoid;
public:
inline ParseContext(JSContext* cx, ParseContext*& parent, SharedContext* sc, ErrorReporter& errorReporter,
class UsedNameTracker& usedNames, Directives* newDirectives, bool isFull)
: Nestable<ParseContext>(&parent),
traceLog_(sc->context,
isFull
? TraceLogger_ParsingFull
: TraceLogger_ParsingSyntax,
errorReporter),
sc_(sc),
errorReporter_(errorReporter),
innermostStatement_(nullptr),
innermostScope_(nullptr),
varScope_(nullptr),
positionalFormalParameterNames_(cx->frontendCollectionPool()),
closedOverBindingsForLazy_(cx->frontendCollectionPool()),
scriptId_(usedNames.nextScriptId()),
isStandaloneFunctionBody_(false),
superScopeNeedsHomeObject_(false),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
innerFunctionsForLazy(cx, GCVector<JSFunction*, 8>(cx)),
newDirectives(newDirectives),
funHasReturnExpr(false),
funHasReturnVoid(false)
{
if (isFunctionBox()) {
if (functionBox()->function()->isNamedLambda())
namedLambdaScope_.emplace(cx, parent, usedNames);
functionScope_.emplace(cx, parent, usedNames);
}
}
MOZ_MUST_USE bool init();
SharedContext* sc() {
return sc_;
}
bool isFunctionBox() const {
return sc_->isFunctionBox();
}
FunctionBox* functionBox() {
return sc_->asFunctionBox();
}
Statement* innermostStatement() {
return innermostStatement_;
}
Scope* innermostScope() {
// There is always at least one scope: the 'var' scope.
MOZ_ASSERT(innermostScope_);
return innermostScope_;
}
Scope& namedLambdaScope() {
MOZ_ASSERT(functionBox()->function()->isNamedLambda());
return *namedLambdaScope_;
}
Scope& functionScope() {
MOZ_ASSERT(isFunctionBox());
return *functionScope_;
}
Scope& varScope() {
MOZ_ASSERT(varScope_);
return *varScope_;
}
bool isFunctionExtraBodyVarScopeInnermost() {
return isFunctionBox() && functionBox()->hasParameterExprs &&
innermostScope() == varScope_;
}
template <typename Predicate /* (Statement*) -> bool */>
Statement* findInnermostStatement(Predicate predicate) {
return Statement::findNearest(innermostStatement_, predicate);
}
template <typename T, typename Predicate /* (Statement*) -> bool */>
T* findInnermostStatement(Predicate predicate) {
return Statement::findNearest<T>(innermostStatement_, predicate);
}
template <typename T>
T* findInnermostStatement() {
return Statement::findNearest<T>(innermostStatement_);
}
AtomVector& positionalFormalParameterNames() {
return *positionalFormalParameterNames_;
}
AtomVector& closedOverBindingsForLazy() {
return *closedOverBindingsForLazy_;
}
// True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level:
//
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel() {
return !innermostStatement_;
}
bool atGlobalLevel() {
return atBodyLevel() && sc_->isGlobalContext();
}
// True if we are at the topmost level of a module only.
bool atModuleLevel() {
return atBodyLevel() && sc_->isModuleContext();
}
void setIsStandaloneFunctionBody() {
isStandaloneFunctionBody_ = true;
}
bool isStandaloneFunctionBody() const {
return isStandaloneFunctionBody_;
}
void setSuperScopeNeedsHomeObject() {
MOZ_ASSERT(sc_->allowSuperProperty());
superScopeNeedsHomeObject_ = true;
}
bool superScopeNeedsHomeObject() const {
return superScopeNeedsHomeObject_;
}
bool useAsmOrInsideUseAsm() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
}
// Most functions start off being parsed as non-generators.
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
// An ES6 generator is marked as a "star generator" before its body is parsed.
GeneratorKind generatorKind() const {
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
}
bool isLegacyGenerator() const {
return generatorKind() == LegacyGenerator;
}
bool isStarGenerator() const {
return generatorKind() == StarGenerator;
}
bool isAsync() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
}
bool needsDotGeneratorName() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
bool isArrowFunction() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isArrow();
}
bool isMethod() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod();
}
bool isGetterOrSetter() const {
return sc_->isFunctionBox() && (sc_->asFunctionBox()->function()->isGetter() ||
sc_->asFunctionBox()->function()->isSetter());
}
uint32_t scriptId() const {
return scriptId_;
}
bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
private:
mozilla::Maybe<DeclarationKind> isVarRedeclaredInInnermostScope(HandlePropertyName name,
DeclarationKind kind);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
enum DryRunOption { NotDryRun, DryRunInnermostScopeOnly };
template <DryRunOption dryRunOption>
bool tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
};
} // namespace frontend
} // namespace js
#endif // frontend_ParseContext_h

View File

@ -368,7 +368,7 @@ bool
ParseContext::init()
{
if (scriptId_ == UINT32_MAX) {
tokenStream_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
errorReporter_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
}
@ -1010,7 +1010,7 @@ Parser<ParseHandler, CharT>::parse()
Directives directives(options().strictOption);
GlobalSharedContext globalsc(context, ScopeKind::Global,
directives, options().extraWarningsOption);
ParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
if (!globalpc.init())
return null();
@ -2151,7 +2151,7 @@ template <>
ParseNode*
Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
{
ParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
if (!evalpc.init())
return nullptr;
@ -2234,7 +2234,7 @@ template <>
ParseNode*
Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc)
{
ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
if (!globalpc.init())
return nullptr;
@ -2272,7 +2272,7 @@ Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
{
MOZ_ASSERT(checkOptionsCalled);
ParseContext modulepc(this, modulesc, nullptr);
SourceParseContext modulepc(this, modulesc, nullptr);
if (!modulepc.init())
return null();
@ -2614,7 +2614,7 @@ Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
return null();
funbox->initStandaloneFunction(enclosingScope);
ParseContext funpc(this, funbox, newDirectives);
SourceParseContext funpc(this, funbox, newDirectives);
if (!funpc.init())
return null();
funpc.setIsStandaloneFunctionBody();
@ -3591,11 +3591,11 @@ Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, Funct
{
// Note that it is possible for outerpc != this->pc, as we may be
// attempting to syntax parse an inner function from an outer full
// parser. In that case, outerpc is a ParseContext from the full parser
// parser. In that case, outerpc is a SourceParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
// Push a new ParseContext.
ParseContext funpc(this, funbox, newDirectives);
SourceParseContext funpc(this, funbox, newDirectives);
if (!funpc.init())
return false;
@ -3619,7 +3619,7 @@ Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, Handl
{
// Note that it is possible for outerpc != this->pc, as we may be
// attempting to syntax parse an inner function from an outer full
// parser. In that case, outerpc is a ParseContext from the full parser
// parser. In that case, outerpc is a SourceParseContext from the full parser
// instead of the current top of the stack of the syntax parser.
FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
@ -3682,7 +3682,7 @@ Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun,
funbox->initFromLazyFunction();
Directives newDirectives = directives;
ParseContext funpc(this, funbox, &newDirectives);
SourceParseContext funpc(this, funbox, &newDirectives);
if (!funpc.init())
return null();
@ -8695,7 +8695,7 @@ Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
genFunbox->isGenexpLambda = true;
genFunbox->initWithEnclosingParseContext(outerpc, Expression);
ParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
SourceParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
if (!genpc.init())
return null();
genpc.functionScope().useAsVarScope(&genpc);

View File

@ -22,6 +22,7 @@
#include "frontend/LanguageExtensions.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/NameCollections.h"
#include "frontend/ParseContext.h"
#include "frontend/SharedContext.h"
#include "frontend/SyntaxParseHandler.h"
@ -35,523 +36,15 @@ class ParserBase;
template <class ParseHandler, typename CharT> class Parser;
/*
* The struct ParseContext stores information about the current parsing context,
* which is part of the parser state (see the field Parser::pc). The current
* parsing context is either the global context, or the function currently being
* parsed. When the parser encounters a function definition, it creates a new
* ParseContext, makes it the new current context.
*/
class ParseContext : public Nestable<ParseContext>
class SourceParseContext: public ParseContext
{
public:
// The intra-function statement stack.
//
// Used for early error checking that depend on the nesting structure of
// statements, such as continue/break targets, labels, and unbraced
// lexical declarations.
class Statement : public Nestable<Statement>
{
StatementKind kind_;
public:
using Nestable<Statement>::enclosing;
using Nestable<Statement>::findNearest;
Statement(ParseContext* pc, StatementKind kind)
: Nestable<Statement>(&pc->innermostStatement_),
kind_(kind)
{ }
template <typename T> inline bool is() const;
template <typename T> inline T& as();
StatementKind kind() const {
return kind_;
}
void refineForKind(StatementKind newForKind) {
MOZ_ASSERT(kind_ == StatementKind::ForLoop);
MOZ_ASSERT(newForKind == StatementKind::ForInLoop ||
newForKind == StatementKind::ForOfLoop);
kind_ = newForKind;
}
};
class LabelStatement : public Statement
{
RootedAtom label_;
public:
LabelStatement(ParseContext* pc, JSAtom* label)
: Statement(pc, StatementKind::Label),
label_(pc->sc_->context, label)
{ }
HandleAtom label() const {
return label_;
}
};
struct ClassStatement : public Statement
{
FunctionBox* constructorBox;
explicit ClassStatement(ParseContext* pc)
: Statement(pc, StatementKind::Class),
constructorBox(nullptr)
{ }
};
// The intra-function scope stack.
//
// Tracks declared and used names within a scope.
class Scope : public Nestable<Scope>
{
// Names declared in this scope. Corresponds to the union of
// VarDeclaredNames and LexicallyDeclaredNames in the ES spec.
//
// A 'var' declared name is a member of the declared name set of every
// scope in its scope contour.
//
// A lexically declared name is a member only of the declared name set of
// the scope in which it is declared.
PooledMapPtr<DeclaredNameMap> declared_;
// FunctionBoxes in this scope that need to be considered for Annex
// B.3.3 semantics. This is checked on Scope exit, as by then we have
// all the declared names and would know if Annex B.3.3 is applicable.
PooledVectorPtr<FunctionBoxVector> possibleAnnexBFunctionBoxes_;
// Monotonically increasing id.
uint32_t id_;
bool maybeReportOOM(ParseContext* pc, bool result) {
if (!result)
ReportOutOfMemory(pc->sc()->context);
return result;
}
public:
using DeclaredNamePtr = DeclaredNameMap::Ptr;
using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
using Nestable<Scope>::enclosing;
explicit inline Scope(ParserBase* parser);
void dump(ParseContext* pc);
uint32_t id() const {
return id_;
}
MOZ_MUST_USE bool init(ParseContext* pc) {
if (id_ == UINT32_MAX) {
pc->tokenStream_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
}
return declared_.acquire(pc->sc()->context);
}
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
return declared_->lookup(name);
}
AddDeclaredNamePtr lookupDeclaredNameForAdd(JSAtom* name) {
return declared_->lookupForAdd(name);
}
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name,
DeclarationKind kind, uint32_t pos)
{
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
MOZ_MUST_USE bool addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox);
// Check if the candidate function boxes for Annex B.3.3 should in
// fact get Annex B semantics. Checked on Scope exit.
MOZ_MUST_USE bool propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc);
// Add and remove catch parameter names. Used to implement the odd
// semantics of catch bodies.
bool addCatchParameters(ParseContext* pc, Scope& catchParamScope);
void removeCatchParameters(ParseContext* pc, Scope& catchParamScope);
void useAsVarScope(ParseContext* pc) {
MOZ_ASSERT(!pc->varScope_);
pc->varScope_ = this;
}
// An iterator for the set of names a scope binds: the set of all
// declared names for 'var' scopes, and the set of lexically declared
// names for non-'var' scopes.
class BindingIter
{
friend class Scope;
DeclaredNameMap::Range declaredRange_;
mozilla::DebugOnly<uint32_t> count_;
bool isVarScope_;
BindingIter(Scope& scope, bool isVarScope)
: declaredRange_(scope.declared_->all()),
count_(0),
isVarScope_(isVarScope)
{
settle();
}
void settle() {
// Both var and lexically declared names are binding in a var
// scope.
if (isVarScope_)
return;
// Otherwise, pop only lexically declared names are
// binding. Pop the range until we find such a name.
while (!declaredRange_.empty()) {
if (BindingKindIsLexical(kind()))
break;
declaredRange_.popFront();
}
}
public:
bool done() const {
return declaredRange_.empty();
}
explicit operator bool() const {
return !done();
}
JSAtom* name() {
MOZ_ASSERT(!done());
return declaredRange_.front().key();
}
DeclarationKind declarationKind() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->kind();
}
BindingKind kind() {
return DeclarationKindToBindingKind(declarationKind());
}
bool closedOver() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->closedOver();
}
void setClosedOver() {
MOZ_ASSERT(!done());
return declaredRange_.front().value()->setClosedOver();
}
void operator++(int) {
MOZ_ASSERT(!done());
MOZ_ASSERT(count_ != UINT32_MAX);
declaredRange_.popFront();
settle();
}
};
inline BindingIter bindings(ParseContext* pc);
};
class VarScope : public Scope
{
public:
explicit inline VarScope(ParserBase* parser);
};
private:
// Trace logging of parsing time.
AutoFrontendTraceLog traceLog_;
// Context shared between parsing and bytecode generation.
SharedContext* sc_;
// TokenStream used for error reporting.
TokenStreamAnyChars& tokenStream_;
// The innermost statement, i.e., top of the statement stack.
Statement* innermostStatement_;
// The innermost scope, i.e., top of the scope stack.
//
// The outermost scope in the stack is usually varScope_. In the case of
// functions, the outermost scope is functionScope_, which may be
// varScope_. See comment above functionScope_.
Scope* innermostScope_;
// If isFunctionBox() and the function is a named lambda, the DeclEnv
// scope for named lambdas.
mozilla::Maybe<Scope> namedLambdaScope_;
// If isFunctionBox(), the scope for the function. If there are no
// parameter expressions, this is scope for the entire function. If there
// are parameter expressions, this holds the special function names
// ('.this', 'arguments') and the formal parameers.
mozilla::Maybe<Scope> functionScope_;
// The body-level scope. This always exists, but not necessarily at the
// beginning of parsing the script in the case of functions with parameter
// expressions.
Scope* varScope_;
// Simple formal parameter names, in order of appearance. Only used when
// isFunctionBox().
PooledVectorPtr<AtomVector> positionalFormalParameterNames_;
// Closed over binding names, in order of appearance. Null-delimited
// between scopes. Only used when syntax parsing.
PooledVectorPtr<AtomVector> closedOverBindingsForLazy_;
// Monotonically increasing id.
uint32_t scriptId_;
// Set when compiling a function using Parser::standaloneFunctionBody via
// the Function or Generator constructor.
bool isStandaloneFunctionBody_;
// Set when encountering a super.property inside a method. We need to mark
// the nearest super scope as needing a home object.
bool superScopeNeedsHomeObject_;
public:
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
// lastAwaitOffset stores the offset of the last await that was parsed.
// NoAwaitOffset is its initial value.
static const uint32_t NoAwaitOffset = UINT32_MAX;
uint32_t lastAwaitOffset;
// All inner functions in this context. Only used when syntax parsing.
Rooted<GCVector<JSFunction*, 8>> innerFunctionsForLazy;
// In a function context, points to a Directive struct that can be updated
// to reflect new directives encountered in the Directive Prologue that
// require reparsing the function. In global/module/generator-tail contexts,
// we don't need to reparse when encountering a DirectivePrologue so this
// pointer may be nullptr.
Directives* newDirectives;
// Set when parsing a function and it has 'return <expr>;'
bool funHasReturnExpr;
// Set when parsing a function and it has 'return;'
bool funHasReturnVoid;
public:
template <class ParseHandler, typename CharT>
ParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
: Nestable<ParseContext>(&prs->pc),
traceLog_(sc->context,
mozilla::IsSame<ParseHandler, FullParseHandler>::value
? TraceLogger_ParsingFull
: TraceLogger_ParsingSyntax,
prs->tokenStream),
sc_(sc),
tokenStream_(prs->tokenStream),
innermostStatement_(nullptr),
innermostScope_(nullptr),
varScope_(nullptr),
positionalFormalParameterNames_(prs->context->frontendCollectionPool()),
closedOverBindingsForLazy_(prs->context->frontendCollectionPool()),
scriptId_(prs->usedNames.nextScriptId()),
isStandaloneFunctionBody_(false),
superScopeNeedsHomeObject_(false),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
innerFunctionsForLazy(prs->context, GCVector<JSFunction*, 8>(prs->context)),
newDirectives(newDirectives),
funHasReturnExpr(false),
funHasReturnVoid(false)
{
if (isFunctionBox()) {
if (functionBox()->function()->isNamedLambda())
namedLambdaScope_.emplace(prs);
functionScope_.emplace(prs);
}
}
MOZ_MUST_USE bool init();
SharedContext* sc() {
return sc_;
}
bool isFunctionBox() const {
return sc_->isFunctionBox();
}
FunctionBox* functionBox() {
return sc_->asFunctionBox();
}
Statement* innermostStatement() {
return innermostStatement_;
}
Scope* innermostScope() {
// There is always at least one scope: the 'var' scope.
MOZ_ASSERT(innermostScope_);
return innermostScope_;
}
Scope& namedLambdaScope() {
MOZ_ASSERT(functionBox()->function()->isNamedLambda());
return *namedLambdaScope_;
}
Scope& functionScope() {
MOZ_ASSERT(isFunctionBox());
return *functionScope_;
}
Scope& varScope() {
MOZ_ASSERT(varScope_);
return *varScope_;
}
bool isFunctionExtraBodyVarScopeInnermost() {
return isFunctionBox() && functionBox()->hasParameterExprs &&
innermostScope() == varScope_;
}
template <typename Predicate /* (Statement*) -> bool */>
Statement* findInnermostStatement(Predicate predicate) {
return Statement::findNearest(innermostStatement_, predicate);
}
template <typename T, typename Predicate /* (Statement*) -> bool */>
T* findInnermostStatement(Predicate predicate) {
return Statement::findNearest<T>(innermostStatement_, predicate);
}
template <typename T>
T* findInnermostStatement() {
return Statement::findNearest<T>(innermostStatement_);
}
AtomVector& positionalFormalParameterNames() {
return *positionalFormalParameterNames_;
}
AtomVector& closedOverBindingsForLazy() {
return *closedOverBindingsForLazy_;
}
// True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level:
//
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel() {
return !innermostStatement_;
}
bool atGlobalLevel() {
return atBodyLevel() && sc_->isGlobalContext();
}
// True if we are at the topmost level of a module only.
bool atModuleLevel() {
return atBodyLevel() && sc_->isModuleContext();
}
void setIsStandaloneFunctionBody() {
isStandaloneFunctionBody_ = true;
}
bool isStandaloneFunctionBody() const {
return isStandaloneFunctionBody_;
}
void setSuperScopeNeedsHomeObject() {
MOZ_ASSERT(sc_->allowSuperProperty());
superScopeNeedsHomeObject_ = true;
}
bool superScopeNeedsHomeObject() const {
return superScopeNeedsHomeObject_;
}
bool useAsmOrInsideUseAsm() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
}
// Most functions start off being parsed as non-generators.
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
// An ES6 generator is marked as a "star generator" before its body is parsed.
GeneratorKind generatorKind() const {
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
}
bool isLegacyGenerator() const {
return generatorKind() == LegacyGenerator;
}
bool isStarGenerator() const {
return generatorKind() == StarGenerator;
}
bool isAsync() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
}
bool needsDotGeneratorName() const {
return isStarGenerator() || isLegacyGenerator() || isAsync();
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
bool isArrowFunction() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isArrow();
}
bool isMethod() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod();
}
bool isGetterOrSetter() const {
return sc_->isFunctionBox() && (sc_->asFunctionBox()->function()->isGetter() ||
sc_->asFunctionBox()->function()->isSetter());
}
uint32_t scriptId() const {
return scriptId_;
}
bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
private:
mozilla::Maybe<DeclarationKind> isVarRedeclaredInInnermostScope(HandlePropertyName name,
DeclarationKind kind);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
enum DryRunOption { NotDryRun, DryRunInnermostScopeOnly };
template <DryRunOption dryRunOption>
bool tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
public:
template<typename ParseHandler, typename CharT>
SourceParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
: ParseContext(prs->context, prs->pc, sc, prs->tokenStream,
prs->usedNames, newDirectives, mozilla::IsSame<ParseHandler,
FullParseHandler>::value)
{ }
};
template <>
@ -619,158 +112,6 @@ enum InHandling { InAllowed, InProhibited };
enum DefaultHandling { NameRequired, AllowDefaultName };
enum TripledotHandling { TripledotAllowed, TripledotProhibited };
// A data structure for tracking used names per parsing session in order to
// compute which bindings are closed over. Scripts and scopes are numbered
// monotonically in textual order and name uses are tracked by lists of
// (script id, scope id) pairs of their use sites.
//
// Intuitively, in a pair (P,S), P tracks the most nested function that has a
// use of u, and S tracks the most nested scope that is still being parsed.
//
// P is used to answer the question "is u used by a nested function?"
// S is used to answer the question "is u used in any scopes currently being
// parsed?"
//
// The algorithm:
//
// Let Used by a map of names to lists.
//
// 1. Number all scopes in monotonic increasing order in textual order.
// 2. Number all scripts in monotonic increasing order in textual order.
// 3. When an identifier u is used in scope numbered S in script numbered P,
// and u is found in Used,
// a. Append (P,S) to Used[u].
// b. Otherwise, assign the the list [(P,S)] to Used[u].
// 4. When we finish parsing a scope S in script P, for each declared name d in
// Declared(S):
// a. If d is found in Used, mark d as closed over if there is a value
// (P_d, S_d) in Used[d] such that P_d > P and S_d > S.
// b. Remove all values (P_d, S_d) in Used[d] such that S_d are >= S.
//
// Steps 1 and 2 are implemented by UsedNameTracker::next{Script,Scope}Id.
// Step 3 is implemented by UsedNameTracker::noteUsedInScope.
// Step 4 is implemented by UsedNameTracker::noteBoundInScope and
// Parser::propagateFreeNamesAndMarkClosedOverBindings.
class UsedNameTracker
{
public:
struct Use
{
uint32_t scriptId;
uint32_t scopeId;
};
class UsedNameInfo
{
friend class UsedNameTracker;
Vector<Use, 6> uses_;
void resetToScope(uint32_t scriptId, uint32_t scopeId);
public:
explicit UsedNameInfo(JSContext* cx)
: uses_(cx)
{ }
UsedNameInfo(UsedNameInfo&& other)
: uses_(mozilla::Move(other.uses_))
{ }
bool noteUsedInScope(uint32_t scriptId, uint32_t scopeId) {
if (uses_.empty() || uses_.back().scopeId < scopeId)
return uses_.append(Use { scriptId, scopeId });
return true;
}
void noteBoundInScope(uint32_t scriptId, uint32_t scopeId, bool* closedOver) {
*closedOver = false;
while (!uses_.empty()) {
Use& innermost = uses_.back();
if (innermost.scopeId < scopeId)
break;
if (innermost.scriptId > scriptId)
*closedOver = true;
uses_.popBack();
}
}
bool isUsedInScript(uint32_t scriptId) const {
return !uses_.empty() && uses_.back().scriptId >= scriptId;
}
};
using UsedNameMap = HashMap<JSAtom*,
UsedNameInfo,
DefaultHasher<JSAtom*>>;
private:
// The map of names to chains of uses.
UsedNameMap map_;
// Monotonically increasing id for all nested scripts.
uint32_t scriptCounter_;
// Monotonically increasing id for all nested scopes.
uint32_t scopeCounter_;
public:
explicit UsedNameTracker(JSContext* cx)
: map_(cx),
scriptCounter_(0),
scopeCounter_(0)
{ }
MOZ_MUST_USE bool init() {
return map_.init();
}
uint32_t nextScriptId() {
MOZ_ASSERT(scriptCounter_ != UINT32_MAX,
"ParseContext::Scope::init should have prevented wraparound");
return scriptCounter_++;
}
uint32_t nextScopeId() {
MOZ_ASSERT(scopeCounter_ != UINT32_MAX);
return scopeCounter_++;
}
UsedNameMap::Ptr lookup(JSAtom* name) const {
return map_.lookup(name);
}
MOZ_MUST_USE bool noteUse(JSContext* cx, JSAtom* name,
uint32_t scriptId, uint32_t scopeId);
struct RewindToken
{
private:
friend class UsedNameTracker;
uint32_t scriptId;
uint32_t scopeId;
};
RewindToken getRewindToken() const {
RewindToken token;
token.scriptId = scriptCounter_;
token.scopeId = scopeCounter_;
return token;
}
// Resets state so that scriptId and scopeId are the innermost script and
// scope, respectively. Used for rewinding state on syntax parse failure.
void rewind(RewindToken token);
// Resets state to beginning of compilation.
void reset() {
map_.clear();
RewindToken token;
token.scriptId = 0;
token.scopeId = 0;
rewind(token);
}
};
template <class Parser>
class AutoAwaitIsKeyword;
@ -973,6 +314,14 @@ ParseContext::Scope::Scope(ParserBase* parser)
id_(parser->usedNames.nextScopeId())
{ }
inline
ParseContext::Scope::Scope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames)
: Nestable<Scope>(&pc->innermostScope_),
declared_(cx->frontendCollectionPool()),
possibleAnnexBFunctionBoxes_(cx->frontendCollectionPool()),
id_(usedNames.nextScopeId())
{ }
inline
ParseContext::VarScope::VarScope(ParserBase* parser)
: Scope(parser)

View File

@ -736,6 +736,25 @@ TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err, uint32_t offset)
return true;
}
bool
TokenStreamAnyChars::hasTokenizationStarted() const
{
return isCurrentTokenType(TOK_EOF) && !isEOF();
}
void
TokenStreamAnyChars::lineNumAndColumnIndex(size_t offset, uint32_t* line, uint32_t* column) const
{
srcCoords.lineNumAndColumnIndex(offset, line, column);
}
size_t
TokenStreamAnyChars::offset() const
{
// Default implementation. Overridden by `TokenStream`.
return 0;
}
bool
TokenStream::computeErrorMetadata(ErrorMetadata* err, uint32_t offset)
{
@ -807,6 +826,13 @@ TokenStream::computeLineOfContext(ErrorMetadata* err, uint32_t offset)
return true;
}
size_t
TokenStream::offset() const
{
return userbuf.offset();
}
bool
TokenStream::reportStrictModeError(unsigned errorNumber, ...)
{

View File

@ -23,6 +23,7 @@
#include "jscntxt.h"
#include "jspubtd.h"
#include "frontend/ErrorReporter.h"
#include "frontend/TokenKind.h"
#include "js/UniquePtr.h"
#include "js/Vector.h"
@ -264,7 +265,7 @@ class StrictModeGetter {
virtual bool strictMode() = 0;
};
class TokenStreamAnyChars
class TokenStreamAnyChars: public ErrorReporter
{
protected:
TokenStreamAnyChars(JSContext* cx, const ReadOnlyCompileOptions& options, StrictModeGetter* smg);
@ -281,7 +282,6 @@ class TokenStreamAnyChars
return currentToken().type == type;
}
const char* getFilename() const { return filename; }
bool getMutedErrors() const { return mutedErrors; }
JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return options().version; }
@ -549,7 +549,7 @@ class TokenStreamAnyChars
return cx;
}
const ReadOnlyCompileOptions& options() const {
virtual const ReadOnlyCompileOptions& options() const override final {
return options_;
}
@ -573,11 +573,15 @@ class TokenStreamAnyChars
MOZ_MUST_USE bool compileWarning(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
unsigned flags, unsigned errorNumber, va_list args);
void reportErrorNoOffset(unsigned errorNumber, ...);
// Compute error metadata for an error at no offset.
void computeErrorMetadataNoOffset(ErrorMetadata* err);
virtual const char* getFilename() const override { return filename; }
virtual bool hasTokenizationStarted() const override;
virtual void lineNumAndColumnIndex(size_t offset, uint32_t* line, uint32_t* column) const override;
virtual void reportErrorNoOffset(unsigned errorNumber, ...) override;
virtual size_t offset() const override;
protected:
// Options used for parsing/tokenizing.
const ReadOnlyCompileOptions& options_;
@ -1101,6 +1105,9 @@ class MOZ_STACK_CLASS TokenStream final : public TokenStreamAnyChars
TokenBuf userbuf; // user input buffer
CharBuffer tokenbuf; // current token string buffer
public:
virtual size_t offset() const override;
};
extern const char*

View File

@ -7126,7 +7126,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);
Directives newDirectives = directives;
ParseContext funpc(&m.parser(), funbox, &newDirectives);
SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
if (!funpc.init())
return false;