Bug 880538 - Generalize strict-mode reparsing (r=jorendorff)
authorLuke Wagner <luke@mozilla.com>
Tue, 25 Jun 2013 10:28:04 -0700
changeset 138627 d9a84be1a35c5991df8c08aa6e5383e161f646bf
parent 138626 70f4d118d159bac1df611a688aa108334f158977
child 138628 6c3396af4b17b4339e3bd43a4ee1e7eb07a00c8c
push id24964
push userryanvm@gmail.com
push dateTue, 16 Jul 2013 20:04:09 +0000
treeherderautoland@fd10ead17ace [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs880538
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 880538 - Generalize strict-mode reparsing (r=jorendorff)
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -200,18 +200,18 @@ frontend::CompileScript(JSContext *cx, H
                                (LazyScript *) NULL);
     }
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
                                     options, chars, length, /* foldConstants = */ true,
                                     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 &&
         evalCaller &&
         (evalCaller->function() || evalCaller->savedCallerFun);
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
                                                   options, staticLevel, sourceObject, 0, length));
     if (!script)
@@ -235,17 +235,18 @@ frontend::CompileScript(JSContext *cx, H
     if (!bce.init())
         return NULL;
 
     // Syntax parsing may cause us to restart processing of top level
     // statements in the script. Use Maybe<> so that the parse context can be
     // 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;
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
     if (evalCaller && evalCaller->strict)
         globalsc.strict = true;
 
     if (options.compileAndGo) {
@@ -262,17 +263,18 @@ frontend::CompileScript(JSContext *cx, H
 
         if (evalCaller && evalCaller->functionOrCallerFunction()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
              * function captured in case it refers to an upvar, and someone
              * 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);
         }
     }
 
     bool canHaveDirectives = true;
     for (;;) {
@@ -300,17 +302,17 @@ frontend::CompileScript(JSContext *cx, H
 
                 // Destroying the parse context will destroy its free
                 // variables, so check if any deoptimization is needed.
                 if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref()))
                     return NULL;
 
                 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());
                 pn = parser.statement();
             }
             if (!pn) {
                 JS_ASSERT(!parser.hadAbortedSyntaxParse());
                 return NULL;
@@ -457,39 +459,44 @@ frontend::CompileFunctionBody(JSContext 
                                     canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = &sct;
 
     JS_ASSERT(fun);
     JS_ASSERT(fun->isTenured());
 
     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;
 
         if (parser.hadAbortedSyntaxParse()) {
             // Hit some unrecoverable ambiguity during an inner syntax parse.
             // Syntax parsing has now been disabled in the parser, so retry
             // 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);
     }
 
     if (!NameFunctions(cx, fn))
         return false;
 
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -379,17 +379,18 @@ Parser<FullParseHandler>::cloneParseTree
     switch (pn->getArity()) {
 #define NULLCHECK(e)    JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
 
       case PN_CODE:
         if (pn->getKind() == PNK_MODULE) {
             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;
         pn->pn_blockid = opn->pn_blockid;
         break;
 
       case PN_LIST:
         pn->makeEmpty();
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -448,19 +448,20 @@ Parser<ParseHandler>::newObjectBox(JSObj
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 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),
     asmStart(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     useAsm(false),
@@ -511,45 +512,45 @@ FunctionBox::FunctionBox(ExclusiveContex
         FunctionBox *parent = outerpc->sc->asFunctionBox();
         if (parent && parent->inWith)
             inWith = true;
     }
 }
 
 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));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     FunctionBox *funbox =
         alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
-                                strict, options().extraWarningsOption);
+                                inheritedDirectives, options().extraWarningsOption);
     if (!funbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
     traceListHead = funbox;
 
     return funbox;
 }
 
 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)
 {
 }
 
 template <>
 ModuleBox *
 Parser<FullParseHandler>::newModuleBox(Module *module, ParseContext<FullParseHandler> *outerpc)
 {
     JS_ASSERT(module && !IsPoisonedPtr(module));
@@ -597,19 +598,21 @@ Parser<ParseHandler>::parse(JSObject *ch
     /*
      * Protect atoms from being collected by a GC activation, which might
      * - nest on this thread due to out of memory (the so-called "last ditch"
      *   GC attempted within js_NewGCThing), or
      * - run for any reason on another thread if this thread is suspended on
      *   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();
 
     Node pn = statements();
     if (pn) {
         if (!tokenStream.matchToken(TOK_EOF)) {
             report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
             return null();
@@ -838,52 +841,48 @@ Parser<ParseHandler>::checkStrictBinding
     }
 
     return true;
 }
 
 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();
 
     ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &handler);
     if (!argsbody)
         return null();
     argsbody->setOp(JSOP_NOP);
     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();
 
     for (unsigned i = 0; i < formals.length(); i++) {
         if (!defineArg(fn, formals[i]))
             return null();
     }
 
     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);
         return null();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
@@ -1793,17 +1792,17 @@ Parser<FullParseHandler>::checkFunctionD
     }
 
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // 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);
 
         if (!addFreeVariablesFromLazyFunction(fun, pc))
             return false;
 
         // The position passed to tokenStream.advance() is relative to
@@ -1928,37 +1927,39 @@ Parser<ParseHandler>::functionDef(Handle
 
     if (bodyProcessed)
         return pn;
 
     RootedFunction fun(context, newFunction(pc, funName, kind));
     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;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
@@ -2049,46 +2050,45 @@ Parser<SyntaxParseHandler>::finishFuncti
     fun->initLazyScript(lazy);
     return true;
 }
 
 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;
 
     // Try a syntax parse for this inner function.
     do {
         Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
         if (!parser)
             break;
 
         {
             // Move the syntax parser to the current position in the stream.
             TokenStream::Position position(keepAtoms);
             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.
                     parser->clearAbortedSyntaxParse();
                     break;
                 }
                 return false;
             }
@@ -2106,22 +2106,22 @@ Parser<FullParseHandler>::functionArgsAn
             return false;
 
         pn->pn_blockid = outerpc->blockid();
         PropagateTransitiveParseFlags(funbox, outerpc->sc);
         return true;
     } 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))
         return false;
 
     pn->pn_blockid = outerpc->blockid();
 
     /*
@@ -2133,34 +2133,33 @@ Parser<FullParseHandler>::functionArgsAn
     PropagateTransitiveParseFlags(funbox, outerpc->sc);
     return true;
 }
 
 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))
         return false;
 
     // This is a lazy function inner to another lazy function. Remember the
     // inner function so that if the outer function is eventually parsed we do
     // not need any further parsing or processing of the inner function.
@@ -2172,27 +2171,33 @@ template <>
 ParseNode *
 Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
                                                  bool strict)
 {
     Node pn = handler.newFunctionDefinition();
     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())) {
             Definition *dn = p.value().get<FullParseHandler>();
             if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                 return NULL;
         }
     }
@@ -2203,17 +2208,18 @@ Parser<FullParseHandler>::standaloneLazy
         return null();
 
     return pn;
 }
 
 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
     // parsing and such.
 
     Node prelude = null();
     bool hasRest;
     if (!functionArguments(kind, &prelude, pn, hasRest))
@@ -2249,22 +2255,18 @@ Parser<ParseHandler>::functionArgsAndBod
     FunctionBodyType bodyType = StatementListBody;
     if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
         tokenStream.ungetToken();
         bodyType = ExpressionBody;
         fun->setIsExprClosure();
     }
 
     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;
 
     if (fun->name() && !checkStrictBinding(fun->name(), pn))
         return false;
 
 #if JS_HAS_EXPR_CLOSURES
@@ -2307,17 +2309,18 @@ Parser<FullParseHandler>::moduleDecl()
     Module *module = Module::create(context, atom);
     if (!module)
         return NULL;
     ModuleBox *modulebox = newModuleBox(module, pc);
     if (!modulebox)
         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);
     pn->pn_body = statements();
     if (!pn->pn_body)
         return NULL;
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_MODULE);
 
@@ -2436,17 +2439,17 @@ Parser<ParseHandler>::maybeParseDirectiv
 
         if (directive == context->names().useStrict) {
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc->setExplicitUseStrict();
             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
                     // one possible strict violation that could occur in the
                     // directive prologue -- octal escapes -- and complain now.
                     if (tokenStream.sawOctalEscape()) {
                         report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
                         return false;
@@ -5979,21 +5982,22 @@ Parser<FullParseHandler>::generatorExpr(
     {
         ParseContext<FullParseHandler> *outerpc = pc;
 
         RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression));
         if (!fun)
             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();
 
         /*
          * We assume conservatively that any deoptimization flags in pc->sc
          * come from the kid. So we propagate these flags into genfn. For code
          * simplicity we also do not detect if the flags were only set in the
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -209,33 +209,37 @@ struct ParseContext : public GenericPars
 
     FuncStmtSet     *funcStmts;     /* Set of (non-top-level) function statements
                                        that will alias any top-level bindings with
                                        the same name. */
 
     // 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
     // context's AtomList, etc. etc.  CheckDestructuring will do that work
     // later.
     //
     // The comments atop CheckDestructuring explain the distinction between
     // assignment-like and declaration-like destructuring patterns, and why
     // 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()
         topStmt(NULL),
         topScopeStmt(NULL),
         blockChain(prs->context),
         staticLevel(staticLevel),
         parenDepth(0),
@@ -245,18 +249,18 @@ struct ParseContext : public GenericPars
         args_(prs->context),
         vars_(prs->context),
         yieldOffset(0),
         parserPC(&prs->pc),
         oldpc(prs->pc),
         lexdeps(prs->context),
         funcStmts(NULL),
         innerFunctions(prs->context),
-        inDeclDestructuring(false),
-        funBecameStrict(false)
+        newDirectives(newDirectives),
+        inDeclDestructuring(false)
     {
         prs->pc = this;
     }
 
     ~ParseContext() {
         // |*parserPC| pointed to this object.  Now that this object is about to
         // die, make |*parserPC| point to this object's parent.
         JS_ASSERT(*parserPC == this);
@@ -284,16 +288,22 @@ struct ParseContext : public GenericPars
     bool atBodyLevel() { return !topStmt; }
 
     inline bool useAsmOrInsideUseAsm() const {
         return sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
     }
 };
 
 template <typename ParseHandler>
+inline
+Directives::Directives(ParseContext<ParseHandler> *parent)
+  : strict_(parent->sc->strict)
+{}
+
+template <typename ParseHandler>
 struct BindData;
 
 class CompExprTransplanter;
 
 template <typename ParseHandler>
 class GenexpGuard;
 
 enum LetContext { LetExpresion, LetStatement };
@@ -364,17 +374,18 @@ class Parser : private AutoGCRooter, pub
     Node parse(JSObject *chain);
 
     /*
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     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
      * is optional if this is a function expression).
      */
     JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind);
 
     void trace(JSTracer *trc);
@@ -397,31 +408,31 @@ class Parser : private AutoGCRooter, pub
   public:
 
     /* Public entry points for parsing. */
     Node statement(bool canHaveDirectives = false);
     bool maybeParseDirective(Node pn, bool *cont);
 
     // 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.
     Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
 
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
      */
     enum FunctionBodyType { StatementListBody, ExpressionBody };
     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; }
 
     const CompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
@@ -482,17 +493,17 @@ class Parser : private AutoGCRooter, pub
      * Additional JS parsers.
      */
     bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
 
     Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
                      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);
 
     Node condition();
     Node comprehensionTail(Node kid, unsigned blockid, bool isGenexp,
                            ParseContext<ParseHandler> *outerpc,
                            ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
     bool arrayInitializerComprehensionTail(Node pn);
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -135,36 +135,60 @@ class FunctionContextFlags
         needsDeclEnvObject(false),
         argumentsHasLocalBinding(false),
         definitelyNeedsArgsObj(false)
     { }
 };
 
 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
  * the bytecode emitter. Note however, that this information is not shared
  * between the two; they simply reuse the same data structure.
  */
 class SharedContext
 {
   public:
     ExclusiveContext *const context;
     AnyContextFlags anyCxFlags;
     bool strict;
     bool extraWarnings;
 
     // 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)
     {}
 
     virtual ObjectBox *toObjectBox() = 0;
     inline bool isGlobalSharedContext() { return toObjectBox() == NULL; }
     inline bool isModuleBox() { return toObjectBox() && toObjectBox()->isModuleBox(); }
     inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
     inline GlobalSharedContext *asGlobalSharedContext();
@@ -187,18 +211,18 @@ class SharedContext
 
 class GlobalSharedContext : public SharedContext
 {
   private:
     const RootedObject scopeChain_; /* scope chain object for the script */
 
   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)
     {}
 
     ObjectBox *toObjectBox() { return NULL; }
     JSObject *scopeChain() const { return scopeChain_; }
 };
 
 inline GlobalSharedContext *
@@ -244,18 +268,18 @@ class FunctionBox : public ObjectBox, pu
     // Fields for use in heuristics.
     bool            usesArguments:1;  /* contains a free use of 'arguments' */
     bool            usesApply:1;      /* contains an f.apply() call */
 
     FunctionContextFlags funCxFlags;
 
     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>(); }
 
     bool isGenerator()              const { return funCxFlags.isGenerator; }
     bool mightAliasLocals()         const { return funCxFlags.mightAliasLocals; }
     bool hasExtensibleScope()       const { return funCxFlags.hasExtensibleScope; }
     bool needsDeclEnvObject()       const { return funCxFlags.needsDeclEnvObject; }