diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 916df80ff1d..469402b7fa7 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -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 > 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); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 746d0d3104a..73551d4b3ae 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -384,7 +384,8 @@ Parser::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; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0d197f07866..a96e6c98e7c 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -453,9 +453,10 @@ Parser::newObjectBox(JSObject *obj) template FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, - ParseContext *outerpc, bool strict, bool extraWarnings) + ParseContext *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 FunctionBox * -Parser::newFunctionBox(JSFunction *fun, - ParseContext *outerpc, bool strict) +Parser::newFunctionBox(JSFunction *fun, ParseContext *outerpc, + Directives inheritedDirectives) { JS_ASSERT(fun && !IsPoisonedPtr(fun)); @@ -530,7 +531,7 @@ Parser::newFunctionBox(JSFunction *fun, */ FunctionBox *funbox = alloc.new_(context, traceListHead, fun, outerpc, - strict, options().extraWarningsOption); + inheritedDirectives, options().extraWarningsOption); if (!funbox) { js_ReportOutOfMemory(context); return NULL; @@ -544,7 +545,7 @@ Parser::newFunctionBox(JSFunction *fun, ModuleBox::ModuleBox(ExclusiveContext *cx, ObjectBox *traceListHead, Module *module, ParseContext *pc, bool extraWarnings) : ObjectBox(module, traceListHead), - SharedContext(cx, true, extraWarnings) + SharedContext(cx, Directives(/* strict = */ true), extraWarnings) { } @@ -602,9 +603,11 @@ Parser::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 globalpc(this, NULL, &globalsc, /* staticLevel = */ 0, /* bodyid = */ 0); + Directives directives(options().strictOption); + GlobalSharedContext globalsc(context, chain, directives, options().extraWarningsOption); + ParseContext globalpc(this, /* parent = */ NULL, &globalsc, + /* newDirectives = */ NULL, /* staticLevel = */ 0, + /* bodyid = */ 0); if (!globalpc.init()) return null(); @@ -843,11 +846,9 @@ Parser::checkStrictBinding(PropertyName *name, Node pn) template <> ParseNode * Parser::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::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 funpc(this, pc, funbox, /* staticLevel = */ 0, /* bodyid = */ 0); + ParseContext funpc(this, pc, funbox, newDirectives, + /* staticLevel = */ 0, /* bodyid = */ 0); if (!funpc.init()) return null(); @@ -874,11 +876,8 @@ Parser::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::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::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::functionArgsAndBody(ParseNode *pn, HandleFunction fun, FunctionType type, FunctionSyntaxKind kind, - bool strict, bool *becameStrict) + Directives inheritedDirectives, + Directives *newDirectives) { - if (becameStrict) - *becameStrict = false; ParseContext *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::functionArgsAndBody(ParseNode *pn, HandleFunction fun, tokenStream.tell(&position); parser->tokenStream.seek(position, tokenStream); - ParseContext funpc(parser, outerpc, funbox, + ParseContext 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::functionArgsAndBody(ParseNode *pn, HandleFunction fun, } while (false); // Continue doing a full parse for this inner function. - ParseContext funpc(this, pc, funbox, + ParseContext 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::functionArgsAndBody(Node pn, HandleFunction fun, FunctionType type, FunctionSyntaxKind kind, - bool strict, bool *becameStrict) + Directives inheritedDirectives, + Directives *newDirectives) { - if (becameStrict) - *becameStrict = false; ParseContext *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 funpc(this, pc, funbox, + ParseContext 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::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 funpc(this, NULL, funbox, staticLevel, 0); + Directives newDirectives = directives; + ParseContext 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::standaloneLazyFunction(HandleFunction fun, unsigned st template bool Parser::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::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::moduleDecl() return NULL; pn->pn_modulebox = modulebox; - ParseContext modulepc(this, pc, modulebox, pc->staticLevel + 1, pc->blockidGen); + ParseContext 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::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::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 genpc(this, outerpc, genFunbox, + ParseContext genpc(this, outerpc, genFunbox, /* newDirectives = */ NULL, outerpc->staticLevel + 1, outerpc->blockidGen); if (!genpc.init()) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index c4d4722086d..4eb0ec53532 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -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 *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 +inline +Directives::Directives(ParseContext *parent) + : strict_(parent->sc->strict) +{} + template struct BindData; @@ -369,7 +379,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter */ ObjectBox *newObjectBox(JSObject *obj); ModuleBox *newModuleBox(Module *module, ParseContext *pc); - FunctionBox *newFunctionBox(JSFunction *fun, ParseContext *pc, bool strict); + FunctionBox *newFunctionBox(JSFunction *fun, ParseContext *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); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index d89ec2f31fd..f044a2e231c 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -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 explicit Directives(ParseContext *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 FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, - ParseContext *pc, - bool strict, bool extraWarnings); + ParseContext *pc, Directives directives, + bool extraWarnings); ObjectBox *toObjectBox() { return this; } JSFunction *function() const { return &object->as(); }