Bug 880538 - Generalize strict-mode reparsing (r=jorendorff)

--HG--
extra : rebase_source : 423ae5c1f922efa523f07ade811e78a1b243c057
This commit is contained in:
Luke Wagner 2013-06-25 10:28:04 -07:00
parent bb425fd69b
commit 0331d292e4
5 changed files with 140 additions and 93 deletions

View File

@ -205,8 +205,8 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
parser.sct = sct;
GlobalSharedContext globalsc(cx, scopeChain,
options.strictOption, options.extraWarningsOption);
Directives directives(options.strictOption);
GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption);
bool savedCallerFun =
options.compileAndGo &&
@ -240,7 +240,8 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// reset when this occurs.
Maybe<ParseContext<FullParseHandler> > pc;
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc, staticLevel, /* bodyid = */ 0);
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc, (Directives *) NULL,
staticLevel, /* bodyid = */ 0);
if (!pc.ref().init())
return NULL;
@ -267,7 +268,8 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
* wishes to decompile it while it's running.
*/
JSFunction *fun = evalCaller->functionOrCallerFunction();
ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), fun->strict());
Directives directives(/* strict = */ fun->strict());
ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), directives);
if (!funbox)
return NULL;
bce.objectList.add(funbox);
@ -305,7 +307,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
pc.destroy();
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc,
staticLevel, /* bodyid = */ 0);
(Directives *) NULL, staticLevel, /* bodyid = */ 0);
if (!pc.ref().init())
return NULL;
JS_ASSERT(parser.pc == pc.addr());
@ -462,16 +464,19 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
fun->setArgCount(formals.length());
// If the context is strict, immediately parse the body in strict
// mode. Otherwise, we parse it normally. If we see a "use strict"
// directive, we backup and reparse it as strict.
ParseNode *fn;
// Speculatively parse using the default directives implied by the context.
// If a directive is encountered (e.g., "use strict") that changes how the
// function should have been parsed, we backup and reparse with the new set
// of directives.
Directives directives(options.strictOption);
TokenStream::Position start(parser.keepAtoms);
parser.tokenStream.tell(&start);
bool strict = options.strictOption;
bool becameStrict;
ParseNode *fn;
while (true) {
fn = parser.standaloneFunctionBody(fun, formals, strict, &becameStrict);
Directives newDirectives = directives;
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives);
if (fn)
break;
@ -481,10 +486,12 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
// the parse.
parser.clearAbortedSyntaxParse();
} else {
// If the function became strict, reparse in strict mode.
if (strict || !becameStrict || parser.tokenStream.hadError())
if (parser.tokenStream.hadError() || directives == newDirectives)
return false;
strict = true;
// Assignment must be monotonic to prevent reparsing iloops
JS_ASSERT_IF(directives.strict(), newDirectives.strict());
directives = newDirectives;
}
parser.tokenStream.seek(start);

View File

@ -384,7 +384,8 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned");
}
NULLCHECK(pn->pn_funbox =
newFunctionBox(opn->pn_funbox->function(), pc, opn->pn_funbox->strict));
newFunctionBox(opn->pn_funbox->function(), pc,
Directives(/* strict = */ opn->pn_funbox->strict)));
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
pn->pn_cookie = opn->pn_cookie;
pn->pn_dflags = opn->pn_dflags;

View File

@ -453,9 +453,10 @@ Parser<ParseHandler>::newObjectBox(JSObject *obj)
template <typename ParseHandler>
FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *outerpc, bool strict, bool extraWarnings)
ParseContext<ParseHandler> *outerpc, Directives directives,
bool extraWarnings)
: ObjectBox(fun, traceListHead),
SharedContext(cx, strict, extraWarnings),
SharedContext(cx, directives, extraWarnings),
bindings(),
bufStart(0),
bufEnd(0),
@ -516,8 +517,8 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
template <typename ParseHandler>
FunctionBox *
Parser<ParseHandler>::newFunctionBox(JSFunction *fun,
ParseContext<ParseHandler> *outerpc, bool strict)
Parser<ParseHandler>::newFunctionBox(JSFunction *fun, ParseContext<ParseHandler> *outerpc,
Directives inheritedDirectives)
{
JS_ASSERT(fun && !IsPoisonedPtr(fun));
@ -530,7 +531,7 @@ Parser<ParseHandler>::newFunctionBox(JSFunction *fun,
*/
FunctionBox *funbox =
alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
strict, options().extraWarningsOption);
inheritedDirectives, options().extraWarningsOption);
if (!funbox) {
js_ReportOutOfMemory(context);
return NULL;
@ -544,7 +545,7 @@ Parser<ParseHandler>::newFunctionBox(JSFunction *fun,
ModuleBox::ModuleBox(ExclusiveContext *cx, ObjectBox *traceListHead, Module *module,
ParseContext<FullParseHandler> *pc, bool extraWarnings)
: ObjectBox(module, traceListHead),
SharedContext(cx, true, extraWarnings)
SharedContext(cx, Directives(/* strict = */ true), extraWarnings)
{
}
@ -602,9 +603,11 @@ Parser<ParseHandler>::parse(JSObject *chain)
* an object lock before it finishes generating bytecode into a script
* protected from the GC by a root or a stack frame reference.
*/
GlobalSharedContext globalsc(context, chain,
options().strictOption, options().extraWarningsOption);
ParseContext<ParseHandler> globalpc(this, NULL, &globalsc, /* staticLevel = */ 0, /* bodyid = */ 0);
Directives directives(options().strictOption);
GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption);
ParseContext<ParseHandler> globalpc(this, /* parent = */ NULL, &globalsc,
/* newDirectives = */ NULL, /* staticLevel = */ 0,
/* bodyid = */ 0);
if (!globalpc.init())
return null();
@ -843,11 +846,9 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
template <>
ParseNode *
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
bool strict, bool *becameStrict)
Directives inheritedDirectives,
Directives *newDirectives)
{
if (becameStrict)
*becameStrict = false;
Node fn = handler.newFunctionDefinition();
if (!fn)
return null();
@ -859,12 +860,13 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
argsbody->makeEmpty();
fn->pn_body = argsbody;
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, inheritedDirectives);
if (!funbox)
return null();
handler.setFunctionBox(fn, funbox);
ParseContext<FullParseHandler> funpc(this, pc, funbox, /* staticLevel = */ 0, /* bodyid = */ 0);
ParseContext<FullParseHandler> funpc(this, pc, funbox, newDirectives,
/* staticLevel = */ 0, /* bodyid = */ 0);
if (!funpc.init())
return null();
@ -874,11 +876,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
}
ParseNode *pn = functionBody(Statement, StatementListBody);
if (!pn) {
if (becameStrict && pc->funBecameStrict)
*becameStrict = true;
if (!pn)
return null();
}
if (!tokenStream.matchToken(TOK_EOF)) {
report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
@ -1798,7 +1797,7 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
// so we can skip over them after accounting for their free variables.
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
JSFunction *fun = handler.nextLazyInnerFunction();
FunctionBox *funbox = newFunctionBox(fun, pc, /* strict = */ false);
FunctionBox *funbox = newFunctionBox(fun, pc, Directives(/* strict = */ false));
if (!funbox)
return false;
handler.setFunctionBox(pn, funbox);
@ -1933,27 +1932,29 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
if (!fun)
return null();
// If the outer scope is strict, immediately parse the function in strict
// mode. Otherwise, we parse it normally. If we see a "use strict"
// directive, we backup and reparse it as strict.
bool initiallyStrict = pc->sc->strict;
bool becameStrict;
if (!functionArgsAndBody(pn, fun, type, kind, initiallyStrict,
&becameStrict))
{
if (initiallyStrict || !becameStrict || tokenStream.hadError())
// Speculatively parse using the directives of the parent parsing context.
// If a directive is encountered (e.g., "use strict") that changes how the
// function should have been parsed, we backup and reparse with the new set
// of directives.
Directives directives(pc);
Directives newDirectives = directives;
while (true) {
if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives))
break;
if (tokenStream.hadError() || directives == newDirectives)
return null();
// Reparse the function in strict mode.
// Assignment must be monotonic to prevent reparsing iloops
JS_ASSERT_IF(directives.strict(), newDirectives.strict());
directives = newDirectives;
tokenStream.seek(start);
if (funName && tokenStream.getToken() == TOK_ERROR)
return null();
// functionArgsAndBody may have already set pn->pn_body before failing.
handler.setFunctionBody(pn, null());
if (!functionArgsAndBody(pn, fun, type, kind, true))
return null();
}
return pn;
@ -2054,14 +2055,13 @@ template <>
bool
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
Directives inheritedDirectives,
Directives *newDirectives)
{
if (becameStrict)
*becameStrict = false;
ParseContext<FullParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
FunctionBox *funbox = newFunctionBox(fun, pc, inheritedDirectives);
if (!funbox)
return false;
@ -2077,13 +2077,13 @@ Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
tokenStream.tell(&position);
parser->tokenStream.seek(position, tokenStream);
ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox,
ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox, newDirectives,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
fun, type, kind, becameStrict))
fun, type, kind, newDirectives))
{
if (parser->hadAbortedSyntaxParse()) {
// Try again with a full parse.
@ -2111,12 +2111,12 @@ Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
} while (false);
// Continue doing a full parse for this inner function.
ParseContext<FullParseHandler> funpc(this, pc, funbox,
ParseContext<FullParseHandler> funpc(this, pc, funbox, newDirectives,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
return false;
if (!leaveFunction(pn, outerpc, kind))
@ -2138,24 +2138,23 @@ template <>
bool
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
Directives inheritedDirectives,
Directives *newDirectives)
{
if (becameStrict)
*becameStrict = false;
ParseContext<SyntaxParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
FunctionBox *funbox = newFunctionBox(fun, pc, inheritedDirectives);
if (!funbox)
return false;
// Initialize early for possible flags mutation via destructuringExpr.
ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
ParseContext<SyntaxParseHandler> funpc(this, pc, funbox, newDirectives,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, newDirectives))
return false;
if (!leaveFunction(pn, outerpc, kind))
@ -2177,17 +2176,23 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
if (!pn)
return null();
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
Directives directives(/* strict = */ strict);
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, directives);
if (!funbox)
return null();
handler.setFunctionBox(pn, funbox);
ParseContext<FullParseHandler> funpc(this, NULL, funbox, staticLevel, 0);
Directives newDirectives = directives;
ParseContext<FullParseHandler> funpc(this, /* parent = */ NULL, funbox,
&newDirectives, staticLevel, /* bodyid = */ 0);
if (!funpc.init())
return null();
if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, NULL))
if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, &newDirectives)) {
JS_ASSERT(directives == newDirectives);
return null();
}
if (fun->isNamedLambda()) {
if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
@ -2208,7 +2213,8 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
template <typename ParseHandler>
bool
Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
FunctionSyntaxKind kind, bool *becameStrict)
FunctionSyntaxKind kind,
Directives *newDirectives)
{
// Given a properly initialized parse context, try to parse an actual
// function without concern for conversion to strict mode, use of lazy
@ -2254,12 +2260,8 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
}
Node body = functionBody(kind, bodyType);
if (!body) {
// Notify the caller if this function was discovered to be strict.
if (becameStrict && pc->funBecameStrict)
*becameStrict = true;
if (!body)
return false;
}
if (!yieldGuard.empty() && !yieldGuard.ref().checkValidBody(body, JSMSG_YIELD_IN_ARROW))
return false;
@ -2312,7 +2314,8 @@ Parser<FullParseHandler>::moduleDecl()
return NULL;
pn->pn_modulebox = modulebox;
ParseContext<FullParseHandler> modulepc(this, pc, modulebox, pc->staticLevel + 1, pc->blockidGen);
ParseContext<FullParseHandler> modulepc(this, pc, modulebox, /* newDirectives = */ NULL,
pc->staticLevel + 1, pc->blockidGen);
if (!modulepc.init())
return NULL;
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_MODULE);
@ -2441,7 +2444,7 @@ Parser<ParseHandler>::maybeParseDirective(Node pn, bool *cont)
if (!pc->sc->strict) {
if (pc->sc->isFunctionBox()) {
// Request that this function be reparsed as strict.
pc->funBecameStrict = true;
pc->newDirectives->setStrict();
return false;
} else {
// We don't reparse global scopes, so we keep track of the
@ -5984,11 +5987,12 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
return null();
/* Create box for fun->object early to protect against last-ditch GC. */
FunctionBox *genFunbox = newFunctionBox(fun, outerpc, outerpc->sc->strict);
Directives directives(/* strict = */ outerpc->sc->strict);
FunctionBox *genFunbox = newFunctionBox(fun, outerpc, directives);
if (!genFunbox)
return null();
ParseContext<FullParseHandler> genpc(this, outerpc, genFunbox,
ParseContext<FullParseHandler> genpc(this, outerpc, genFunbox, /* newDirectives = */ NULL,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!genpc.init())
return null();

View File

@ -214,6 +214,13 @@ struct ParseContext : public GenericParseContext
// All inner functions in this context. Only filled in when parsing syntax.
AutoFunctionVector innerFunctions;
// 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 NULL.
Directives *newDirectives;
// Set when parsing a declaration-like destructuring pattern. This flag
// causes PrimaryExpr to create PN_NAME parse nodes for variable references
// which are not hooked into any definition's use chain, added to any tree
@ -225,12 +232,9 @@ struct ParseContext : public GenericParseContext
// they need to be treated differently.
bool inDeclDestructuring:1;
// True if we are in a function, saw a "use strict" directive, and weren't
// strict before.
bool funBecameStrict:1;
ParseContext(Parser<ParseHandler> *prs, GenericParseContext *parent,
SharedContext *sc, unsigned staticLevel, uint32_t bodyid)
SharedContext *sc, Directives *newDirectives,
unsigned staticLevel, uint32_t bodyid)
: GenericParseContext(parent, sc),
bodyid(0), // initialized in init()
blockidGen(bodyid), // used to set |bodyid| and subsequently incremented in init()
@ -250,8 +254,8 @@ struct ParseContext : public GenericParseContext
lexdeps(prs->context),
funcStmts(NULL),
innerFunctions(prs->context),
inDeclDestructuring(false),
funBecameStrict(false)
newDirectives(newDirectives),
inDeclDestructuring(false)
{
prs->pc = this;
}
@ -288,6 +292,12 @@ struct ParseContext : public GenericParseContext
}
};
template <typename ParseHandler>
inline
Directives::Directives(ParseContext<ParseHandler> *parent)
: strict_(parent->sc->strict)
{}
template <typename ParseHandler>
struct BindData;
@ -369,7 +379,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter
*/
ObjectBox *newObjectBox(JSObject *obj);
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
FunctionBox *newFunctionBox(JSFunction *fun, ParseContext<ParseHandler> *pc, bool strict);
FunctionBox *newFunctionBox(JSFunction *fun, ParseContext<ParseHandler> *pc,
Directives directives);
/*
* Create a new function object given parse context (pc) and a name (which
@ -402,7 +413,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
// Parse a function, given only its body. Used for the Function constructor.
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
bool strict, bool *becameStrict);
Directives inheritedDirectives, Directives *newDirectives);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
@ -416,7 +427,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
FunctionSyntaxKind kind, bool *becameStrict);
FunctionSyntaxKind kind, Directives *newDirectives);
virtual bool strictMode() { return pc->sc->strict; }
@ -487,7 +498,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
FunctionType type, FunctionSyntaxKind kind);
bool functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict = NULL);
Directives inheritedDirectives, Directives *newDirectives);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);

View File

@ -140,6 +140,30 @@ class FunctionContextFlags
class GlobalSharedContext;
// List of directives that may be encountered in a Directive Prologue (ES5 15.1).
class Directives
{
bool strict_;
public:
explicit Directives(bool strict) : strict_(strict) {}
template <typename ParseHandler> explicit Directives(ParseContext<ParseHandler> *parent);
void setStrict() { strict_ = true; }
bool strict() const { return strict_; }
Directives &operator=(Directives rhs) {
strict_ = rhs.strict_;
return *this;
}
bool operator==(const Directives &rhs) const {
return strict_ == rhs.strict_;
}
bool operator!=(const Directives &rhs) const {
return strict_ != rhs.strict_;
}
};
/*
* The struct SharedContext is part of the current parser context (see
* ParseContext). It stores information that is reused between the parser and
@ -156,10 +180,10 @@ class SharedContext
// If it's function code, funbox must be non-NULL and scopeChain must be NULL.
// If it's global code, funbox must be NULL.
SharedContext(ExclusiveContext *cx, bool strict, bool extraWarnings)
SharedContext(ExclusiveContext *cx, Directives directives, bool extraWarnings)
: context(cx),
anyCxFlags(),
strict(strict),
strict(directives.strict()),
extraWarnings(extraWarnings)
{}
@ -192,8 +216,8 @@ class GlobalSharedContext : public SharedContext
public:
GlobalSharedContext(ExclusiveContext *cx, JSObject *scopeChain,
bool strict, bool extraWarnings)
: SharedContext(cx, strict, extraWarnings),
Directives directives, bool extraWarnings)
: SharedContext(cx, directives, extraWarnings),
scopeChain_(cx, scopeChain)
{}
@ -249,8 +273,8 @@ class FunctionBox : public ObjectBox, public SharedContext
template <typename ParseHandler>
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *pc,
bool strict, bool extraWarnings);
ParseContext<ParseHandler> *pc, Directives directives,
bool extraWarnings);
ObjectBox *toObjectBox() { return this; }
JSFunction *function() const { return &object->as<JSFunction>(); }