Bug 754181 - Don't store the strict mode code flag twice (attempt 2). r=jwalden,luke.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 21 May 2012 20:06:43 -0700
changeset 95231 9cca745f94bf3d584a3af30a7ee5babb74e20ae1
parent 95230 e98f0fc3d02ecebc8dba4449558e673733882bbc
child 95232 faf7c04a53f4c0cb69816c14b576b030b92352c5
push id10017
push usernnethercote@mozilla.com
push dateWed, 30 May 2012 07:03:01 +0000
treeherdermozilla-inbound@9cca745f94bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden, luke
bugs754181
milestone15.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 754181 - Don't store the strict mode code flag twice (attempt 2). r=jwalden,luke.
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/frontend/TreeContext.h
js/src/jsfun.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -119,23 +119,18 @@ frontend::CompileScript(JSContext *cx, J
 
     GlobalScope globalScope(cx, globalObj);
     bce.sc->setScopeChain(scopeChain);
     bce.globalScope = &globalScope;
     if (!SetStaticLevel(bce.sc, staticLevel))
         return NULL;
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
-    if (callerFrame &&
-        callerFrame->isScriptFrame() &&
-        callerFrame->script()->strictModeCode)
-    {
+    if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode)
         bce.sc->setInStrictMode();
-        parser.tokenStream.setStrictMode();
-    }
 
 #ifdef DEBUG
     bool savedCallerFun;
     savedCallerFun = false;
 #endif
     if (compileAndGo) {
         if (source) {
             /*
@@ -266,18 +261,16 @@ frontend::CompileFunctionBody(JSContext 
                               Bindings *bindings, const jschar *chars, size_t length,
                               const char *filename, unsigned lineno, JSVersion version)
 {
     Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version,
                   /* cfp = */ NULL, /* foldConstants = */ true, /* compileAndGo = */ false);
     if (!parser.init())
         return false;
 
-    TokenStream &tokenStream = parser.tokenStream;
-
     SharedContext funsc(cx, /* inFunction = */ true);
 
     TreeContext funtc(&parser, &funsc);
     if (!funtc.init())
         return NULL;
 
     BytecodeEmitter funbce(&parser, &funsc, lineno,
                            /* noScriptRval = */ false, /* needsScriptGlobal = */ false);
@@ -318,17 +311,17 @@ frontend::CompileFunctionBody(JSContext 
 
     /*
      * After we're done parsing, we must fold constants, analyze any nested
      * functions, and generate code for this function, including a stop opcode
      * at the end.
      */
     ParseNode *pn = fn ? parser.functionBody(Parser::StatementListBody) : NULL;
     if (pn) {
-        if (!tokenStream.matchToken(TOK_EOF)) {
+        if (!parser.tokenStream.matchToken(TOK_EOF)) {
             parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
             pn = NULL;
         } else if (!FoldConstants(cx, pn, &parser)) {
             /* FoldConstants reported the error already. */
             pn = NULL;
         } else if (!AnalyzeFunctions(&parser)) {
             pn = NULL;
         } else {
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1197,18 +1197,18 @@ BindNameToSlot(JSContext *cx, BytecodeEm
             return JS_TRUE;
         }
         break;
       default:
         if (pn->isConst()) {
             if (bce->sc->needStrictChecks()) {
                 JSAutoByteString name;
                 if (!js_AtomToPrintableString(cx, atom, &name) ||
-                    !ReportStrictModeError(cx, bce->tokenStream(), bce->sc, pn, JSMSG_READ_ONLY,
-                                           name.ptr())) {
+                    !ReportStrictModeError(cx, bce->tokenStream(), pn, JSMSG_READ_ONLY, name.ptr()))
+                {
                     return JS_FALSE;
                 }
             }
             pn->setOp(op = JSOP_NAME);
         }
     }
 
     if (cookie.isFree()) {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -71,22 +71,29 @@ using namespace js::frontend;
     JS_BEGIN_MACRO                                                                          \
         if (tokenStream.getToken((__flags)) != tt) {                                        \
             reportErrorNumber(NULL, JSREPORT_ERROR, errno);                                 \
             return NULL;                                                                    \
         }                                                                                   \
     JS_END_MACRO
 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
 
+bool
+StrictModeGetter::get() const
+{
+    return parser->tc->sc->inStrictMode();
+}
+
 Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
                const jschar *chars, size_t length, const char *fn, unsigned ln, JSVersion v,
                StackFrame *cfp, bool foldConstants, bool compileAndGo)
   : AutoGCRooter(cx, PARSER),
     context(cx),
-    tokenStream(cx, prin, originPrin, chars, length, fn, ln, v),
+    strictModeGetter(this),
+    tokenStream(cx, prin, originPrin, chars, length, fn, ln, v, &strictModeGetter),
     tempPoolMark(NULL),
     principals(NULL),
     originPrincipals(NULL),
     callerFrame(cfp),
     allocator(cx),
     traceListHead(NULL),
     tc(NULL),
     keepAtoms(cx->runtime),
@@ -98,16 +105,17 @@ Parser::Parser(JSContext *cx, JSPrincipa
     JS_ASSERT_IF(cfp, cfp->isScriptFrame());
 }
 
 bool
 Parser::init()
 {
     if (!context->ensureParseMapPool())
         return false;
+
     tempPoolMark = context->tempLifoAlloc().mark();
     return true;
 }
 
 Parser::~Parser()
 {
     JSContext *cx = context;
     if (principals)
@@ -437,18 +445,18 @@ static bool
 CheckStrictAssignment(JSContext *cx, Parser *parser, ParseNode *lhs)
 {
     if (parser->tc->sc->needStrictChecks() && lhs->isKind(PNK_NAME)) {
         JSAtom *atom = lhs->pn_atom;
         JSAtomState *atomState = &cx->runtime->atomState;
         if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
             JSAutoByteString name;
             if (!js_AtomToPrintableString(cx, atom, &name) ||
-                !ReportStrictModeError(cx, TS(parser), parser->tc->sc, lhs, JSMSG_DEPRECATED_ASSIGN,
-                                       name.ptr())) {
+                !ReportStrictModeError(cx, TS(parser), lhs, JSMSG_DEPRECATED_ASSIGN, name.ptr()))
+            {
                 return false;
             }
         }
     }
     return true;
 }
 
 /*
@@ -466,30 +474,29 @@ CheckStrictBinding(JSContext *cx, Parser
     JSAtomState *atomState = &cx->runtime->atomState;
     if (name == atomState->evalAtom ||
         name == atomState->argumentsAtom ||
         FindKeyword(name->charsZ(), name->length()))
     {
         JSAutoByteString bytes;
         if (!js_AtomToPrintableString(cx, name, &bytes))
             return false;
-        return ReportStrictModeError(cx, TS(parser), parser->tc->sc, pn, JSMSG_BAD_BINDING,
-                                     bytes.ptr());
+        return ReportStrictModeError(cx, TS(parser), pn, JSMSG_BAD_BINDING, bytes.ptr());
     }
 
     return true;
 }
 
 static bool
 ReportBadParameter(JSContext *cx, Parser *parser, JSAtom *name, unsigned errorNumber)
 {
     Definition *dn = parser->tc->decls.lookupFirst(name);
     JSAutoByteString bytes;
     return js_AtomToPrintableString(cx, name, &bytes) &&
-           ReportStrictModeError(cx, TS(parser), parser->tc->sc, dn, errorNumber, bytes.ptr());
+           ReportStrictModeError(cx, TS(parser), dn, errorNumber, bytes.ptr());
 }
 
 /*
  * In strict mode code, all parameter names must be distinct, must not be
  * strict mode reserved keywords, and must not be 'eval' or 'arguments'.  We
  * must perform these checks here, and not eagerly during parsing, because a
  * function's body may turn on strict mode for the function head.
  */
@@ -1761,20 +1768,16 @@ Parser::functionDef(HandlePropertyName f
     JS_ASSERT_IF(!outertc->sc->inFunction && bodyLevel && kind == Statement,
                  pn->pn_cookie.isFree());
 
     pn->pn_blockid = outertc->sc->blockid();
 
     if (!LeaveFunction(pn, this, funName, kind))
         return NULL;
 
-    /* If the surrounding function is not strict code, reset the lexer. */
-    if (!outertc->sc->inStrictMode())
-        tokenStream.setStrictMode(false);
-
     return pn;
 }
 
 ParseNode *
 Parser::functionStmt()
 {
     RootedPropertyName name(context);
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
@@ -1864,17 +1867,16 @@ Parser::recognizeDirectivePrologue(Parse
              *   }
              */
             if (tokenStream.hasOctalCharacterEscape()) {
                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
                 return false;
             }
 
             tc->sc->setInStrictMode();
-            tokenStream.setStrictMode();
         }
     }
     return true;
 }
 
 /*
  * Parse the statements in a block, creating a TOK_LC node that lists the
  * statements' trees.  If called from block-parsing code, the caller must
@@ -2305,17 +2307,17 @@ BindVarOrConst(JSContext *cx, BindData *
 }
 
 static bool
 MakeSetCall(JSContext *cx, ParseNode *pn, Parser *parser, unsigned msg)
 {
     JS_ASSERT(pn->isArity(PN_LIST));
     JS_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_EVAL) ||
               pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
-    if (!ReportStrictModeError(cx, TS(parser), parser->tc->sc, pn, msg))
+    if (!ReportStrictModeError(cx, TS(parser), pn, msg))
         return false;
 
     ParseNode *pn2 = pn->pn_head;
     if (pn2->isKind(PNK_FUNCTION) && (pn2->pn_funbox->inGenexpLambda)) {
         ReportCompileErrorNumber(cx, TS(parser), pn, JSREPORT_ERROR, msg);
         return false;
     }
     pn->pn_xflags |= PNX_SETCALL;
@@ -2866,20 +2868,18 @@ Parser::letBlock(LetContext letContext)
         /*
          * Strict mode eliminates a grammar ambiguity with unparenthesized
          * LetExpressions in an ExpressionStatement. If followed immediately
          * by an arguments list, it's ambiguous whether the let expression
          * is the callee or the call is inside the let expression body.
          *
          * See bug 569464.
          */
-        if (!ReportStrictModeError(context, &tokenStream, tc->sc, pnlet,
-                                   JSMSG_STRICT_CODE_LET_EXPR_STMT)) {
+        if (!ReportStrictModeError(context, &tokenStream, pnlet, JSMSG_STRICT_CODE_LET_EXPR_STMT))
             return NULL;
-        }
 
         /*
          * If this is really an expression in let statement guise, then we
          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
          * the return value of the expression.
          */
         ParseNode *semi = UnaryNode::create(PNK_SEMI, this);
         if (!semi)
@@ -4845,17 +4845,17 @@ Parser::unaryExpr()
                  * because the optimizer will eliminate the useless delete.
                  */
                 if (!MakeSetCall(context, pn2, this, JSMSG_BAD_DELETE_OPERAND))
                     return NULL;
                 pn2->pn_xflags &= ~PNX_SETCALL;
             }
             break;
           case PNK_NAME:
-            if (!ReportStrictModeError(context, &tokenStream, tc->sc, pn,
+            if (!ReportStrictModeError(context, &tokenStream, pn,
                                        JSMSG_DEPRECATED_DELETE_OPERAND)) {
                 return NULL;
             }
             pn2->setOp(JSOP_DELNAME);
             break;
           default:;
         }
         pn->pn_kid = pn2;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -30,16 +30,17 @@ class StaticBlockObject;
 
 enum FunctionSyntaxKind { Expression, Statement };
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
 
 struct Parser : private AutoGCRooter
 {
     JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
+    StrictModeGetter    strictModeGetter; /* used by tokenStream to test for strict mode */
     TokenStream         tokenStream;
     void                *tempPoolMark;  /* initial JSContext.tempLifoAlloc mark */
     JSPrincipals        *principals;    /* principals associated with source */
     JSPrincipals        *originPrincipals;   /* see jsapi.h 'originPrincipals' comment */
     StackFrame          *const callerFrame;  /* scripted caller frame for eval and dbgapi */
     ParseNodeAllocator  allocator;
     ObjectBox           *traceListHead; /* list of parsed object for GC tracing */
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -116,17 +116,17 @@ js::IsIdentifier(JSLinearString *str)
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4351)
 #endif
 
 /* Initialize members that aren't initialized in |init|. */
 TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
                          const jschar *base, size_t length, const char *fn, unsigned ln,
-                         JSVersion v)
+                         JSVersion v, StrictModeGetter *smg)
   : tokens(),
     tokensRoot(cx, &tokens),
     cursor(),
     lookahead(),
     lineno(ln),
     flags(),
     linebase(base),
     prevLinebase(NULL),
@@ -136,24 +136,22 @@ TokenStream::TokenStream(JSContext *cx, 
     userbufRoot(cx, &userbuf),
     filename(fn),
     sourceMap(NULL),
     listenerTSData(),
     tokenbuf(cx),
     version(v),
     xml(VersionHasXML(v)),
     cx(cx),
-    originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin))
+    originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin)),
+    strictModeGetter(smg)
 {
     if (originPrincipals)
         JS_HoldPrincipals(originPrincipals);
 
-    if (cx->hasRunOption(JSOPTION_STRICT_MODE))
-        setStrictMode();
-
     JSSourceHandler listener = cx->runtime->debugHooks.sourceHandler;
     void *listenerData = cx->runtime->debugHooks.sourceHandlerData;
 
     if (listener)
         listener(fn, ln, base, length, &listenerTSData, listenerData);
 
     /*
      * This table holds all the token kinds that satisfy these properties:
@@ -521,55 +519,51 @@ TokenStream::reportCompileErrorNumberVA(
         }
         cx->free_((void *)report.messageArgs);
     }
 
     return warning;
 }
 
 bool
-js::ReportStrictModeError(JSContext *cx, TokenStream *ts, SharedContext *sc, ParseNode *pn,
-                          unsigned errorNumber, ...)
+js::ReportStrictModeError(JSContext *cx, TokenStream *ts, ParseNode *pn, unsigned errorNumber, ...)
 {
-    JS_ASSERT(ts || sc);
     JS_ASSERT(cx == ts->getContext());
 
     /* In strict mode code, this is an error, not merely a warning. */
     unsigned flags;
-    if ((ts && ts->isStrictMode()) || (sc && sc->inStrictMode())) {
+    if (ts->isStrictMode())
         flags = JSREPORT_ERROR;
-    } else {
-        if (!cx->hasStrictOption())
-            return true;
+    else if (cx->hasStrictOption())
         flags = JSREPORT_WARNING;
-    }
+    else
+        return true;
 
     va_list ap;
     va_start(ap, errorNumber);
     bool result = ts->reportCompileErrorNumberVA(pn, flags, errorNumber, ap);
     va_end(ap);
 
     return result;
 }
 
 bool
 js::ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, ParseNode *pn, unsigned flags,
                              unsigned errorNumber, ...)
 {
-    va_list ap;
-
     /*
-     * We don't accept a TreeContext argument, so we can't implement
-     * JSREPORT_STRICT_MODE_ERROR here.  Use ReportStrictModeError instead,
-     * or do the checks in the caller and pass plain old JSREPORT_ERROR.
+     * We don't handle JSREPORT_STRICT_MODE_ERROR here.  Use
+     * ReportStrictModeError instead, or do the checks in the caller and pass
+     * plain old JSREPORT_ERROR.
      */
     JS_ASSERT(!(flags & JSREPORT_STRICT_MODE_ERROR));
+    JS_ASSERT(cx == ts->getContext());
 
+    va_list ap;
     va_start(ap, errorNumber);
-    JS_ASSERT(cx == ts->getContext());
     bool result = ts->reportCompileErrorNumberVA(pn, flags, errorNumber, ap);
     va_end(ap);
 
     return result;
 }
 
 #if JS_HAS_XML_SUPPORT
 
@@ -1302,17 +1296,17 @@ TokenStream::checkForKeyword(const jscha
          * falling through to the code below (ES5 forbids them in strict mode).
          */
         if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
             return true;
     }
 
     /* Strict reserved word. */
     if (isStrictMode())
-        return ReportStrictModeError(cx, this, NULL, NULL, JSMSG_RESERVED_ID, kw->chars);
+        return ReportStrictModeError(cx, this, NULL, JSMSG_RESERVED_ID, kw->chars);
     return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_STRICT | JSREPORT_WARNING,
                                     JSMSG_RESERVED_ID, kw->chars);
 }
 
 enum FirstCharKind {
     Other,
     OneChar,
     Ident,
@@ -1587,17 +1581,17 @@ TokenStream::getTokenInternal()
 
                       default:
                         if ('0' <= c && c < '8') {
                             int32_t val = JS7_UNDEC(c);
 
                             c = peekChar();
                             /* Strict mode code allows only \0, then a non-digit. */
                             if (val != 0 || JS7_ISDEC(c)) {
-                                if (!ReportStrictModeError(cx, this, NULL, NULL,
+                                if (!ReportStrictModeError(cx, this, NULL,
                                                            JSMSG_DEPRECATED_OCTAL)) {
                                     goto error;
                                 }
                                 setOctalCharacterEscape();
                             }
                             if ('0' <= c && c < '8') {
                                 val = 8 * val + JS7_UNDEC(c);
                                 getChar();
@@ -1768,17 +1762,17 @@ TokenStream::getTokenInternal()
             numStart = userbuf.addressOfNextRawChar() - 1;  /* one past the '0x' */
             while (JS7_ISHEX(c))
                 c = getCharIgnoreEOL();
         } else if (JS7_ISDEC(c)) {
             radix = 8;
             numStart = userbuf.addressOfNextRawChar() - 1;  /* one past the '0' */
             while (JS7_ISDEC(c)) {
                 /* Octal integer literals are not permitted in strict mode code. */
-                if (!ReportStrictModeError(cx, this, NULL, NULL, JSMSG_DEPRECATED_OCTAL))
+                if (!ReportStrictModeError(cx, this, NULL, JSMSG_DEPRECATED_OCTAL))
                     goto error;
 
                 /*
                  * Outside strict mode, we permit 08 and 09 as decimal numbers,
                  * which makes our behaviour a superset of the ECMA numeric
                  * grammar. We might not always be so permissive, so we warn
                  * about it.
                  */
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -377,23 +377,22 @@ struct Token {
 
 enum TokenStreamFlags
 {
     TSF_EOF = 0x02,             /* hit end of file */
     TSF_EOL = 0x04,             /* an EOL was hit in whitespace or a multi-line comment */
     TSF_OPERAND = 0x08,         /* looking for operand, not operator */
     TSF_UNEXPECTED_EOF = 0x10,  /* unexpected end of input, i.e. TOK_EOF not at top-level. */
     TSF_KEYWORD_IS_NAME = 0x20, /* Ignore keywords and return TOK_NAME instead to the parser. */
-    TSF_STRICT_MODE_CODE = 0x40,/* Tokenize as appropriate for strict mode code. */
-    TSF_DIRTYLINE = 0x80,       /* non-whitespace since start of line */
-    TSF_OWNFILENAME = 0x100,    /* ts->filename is malloc'd */
-    TSF_XMLTAGMODE = 0x200,     /* scanning within an XML tag in E4X */
-    TSF_XMLTEXTMODE = 0x400,    /* scanning XMLText terminal from E4X */
-    TSF_XMLONLYMODE = 0x800,    /* don't scan {expr} within text/tag */
-    TSF_OCTAL_CHAR = 0x1000,    /* observed a octal character escape */
+    TSF_DIRTYLINE = 0x40,       /* non-whitespace since start of line */
+    TSF_OWNFILENAME = 0x80,     /* ts->filename is malloc'd */
+    TSF_XMLTAGMODE = 0x100,     /* scanning within an XML tag in E4X */
+    TSF_XMLTEXTMODE = 0x200,    /* scanning XMLText terminal from E4X */
+    TSF_XMLONLYMODE = 0x400,    /* don't scan {expr} within text/tag */
+    TSF_OCTAL_CHAR = 0x800,     /* observed a octal character escape */
 
     /*
      * To handle the hard case of contiguous HTML comments, we want to clear the
      * TSF_DIRTYINPUT flag at the end of each such comment.  But we'd rather not
      * scan for --> within every //-style comment unless we have to.  So we set
      * TSF_IN_HTML_COMMENT when a <!-- is scanned as an HTML begin-comment, and
      * clear it (and TSF_DIRTYINPUT) when we scan --> either on a clean line, or
      * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment.
@@ -404,17 +403,35 @@ enum TokenStreamFlags
      *      <!-- comment hiding hack #1
      *      code goes here
      *      // --> oops, markup for script-unaware browsers goes here!
      *    </script>
      *
      * It does not cope with malformed comment hiding hacks where --> is hidden
      * by C-style comments, or on a dirty line.  Such cases are already broken.
      */
-    TSF_IN_HTML_COMMENT = 0x2000
+    TSF_IN_HTML_COMMENT = 0x1000
+};
+
+struct Parser;
+
+// Ideally, tokenizing would be entirely independent of context.  But the
+// strict mode flag, which is in SharedContext, affects tokenizing, and
+// TokenStream needs to see it.
+//
+// This class constitutes a tiny back-channel from TokenStream to the strict
+// mode flag that avoids exposing the rest of SharedContext to TokenStream.  
+// get() is implemented in Parser.cpp.
+//
+class StrictModeGetter {
+    Parser *parser;
+  public:
+    StrictModeGetter(Parser *p) : parser(p) { }
+
+    bool get() const;
 };
 
 class TokenStream
 {
     /* Unicode separators that are treated as line terminators, in addition to \n, \r */
     enum {
         LINE_SEPARATOR = 0x2028,
         PARA_SEPARATOR = 0x2029
@@ -422,19 +439,20 @@ class TokenStream
 
     static const size_t ntokens = 4;                /* 1 current + 2 lookahead, rounded
                                                        to power of 2 to avoid divmod by 3 */
     static const unsigned ntokensMask = ntokens - 1;
 
   public:
     typedef Vector<jschar, 32> CharBuffer;
 
-    TokenStream(JSContext *, JSPrincipals *principals, JSPrincipals *originPrincipals,
+    TokenStream(JSContext *cx, JSPrincipals *principals, JSPrincipals *originPrincipals,
                 const jschar *base, size_t length, const char *filename, unsigned lineno,
-                JSVersion version);
+                JSVersion version, StrictModeGetter *smg);
+
     ~TokenStream();
 
     /* Accessors. */
     JSContext *getContext() const { return cx; }
     bool onCurrentLine(const TokenPos &pos) const { return lineno == pos.end.lineno; }
     const Token &currentToken() const { return tokens[cursor]; }
     bool isCurrentTokenType(TokenKind type) const {
         return currentToken().type == type;
@@ -464,23 +482,22 @@ class TokenStream
         return TokenKindIsShift(currentToken().type);
     }
 
     bool isCurrentTokenAssignment() const {
         return TokenKindIsAssignment(currentToken().type);
     }
 
     /* Flag methods. */
-    void setStrictMode(bool enabled = true) { setFlag(enabled, TSF_STRICT_MODE_CODE); }
     void setXMLTagMode(bool enabled = true) { setFlag(enabled, TSF_XMLTAGMODE); }
     void setXMLOnlyMode(bool enabled = true) { setFlag(enabled, TSF_XMLONLYMODE); }
     void setUnexpectedEOF(bool enabled = true) { setFlag(enabled, TSF_UNEXPECTED_EOF); }
     void setOctalCharacterEscape(bool enabled = true) { setFlag(enabled, TSF_OCTAL_CHAR); }
 
-    bool isStrictMode() { return !!(flags & TSF_STRICT_MODE_CODE); }
+    bool isStrictMode() { return strictModeGetter ? strictModeGetter->get() : false; }
     bool isXMLTagMode() { return !!(flags & TSF_XMLTAGMODE); }
     bool isXMLOnlyMode() { return !!(flags & TSF_XMLONLYMODE); }
     bool isUnexpectedEOF() { return !!(flags & TSF_UNEXPECTED_EOF); }
     bool isEOF() const { return !!(flags & TSF_EOF); }
     bool hasOctalCharacterEscape() const { return flags & TSF_OCTAL_CHAR; }
 
     bool reportCompileErrorNumberVA(ParseNode *pn, unsigned flags, unsigned errorNumber, va_list ap);
 
@@ -777,16 +794,17 @@ class TokenStream
     CharBuffer          tokenbuf;       /* current token string buffer */
     int8_t              oneCharTokens[128];  /* table of one-char tokens */
     bool                maybeEOL[256];       /* probabilistic EOL lookup table */
     bool                maybeStrSpecial[256];/* speeds up string scanning */
     JSVersion           version;        /* (i.e. to identify keywords) */
     bool                xml;            /* see JSOPTION_XML */
     JSContext           *const cx;
     JSPrincipals        *const originPrincipals;
+    StrictModeGetter    *strictModeGetter; /* used to test for strict mode */
 };
 
 struct KeywordInfo {
     const char  *chars;         /* C string with keyword text */
     TokenKind   tokentype;
     JSOp        op;             /* JSOp */
     JSVersion   version;        /* JSVersion */
 };
@@ -817,34 +835,20 @@ IsIdentifier(JSLinearString *str);
  * Otherwise use ts, which must not be null.
  */
 bool
 ReportCompileErrorNumber(JSContext *cx, TokenStream *ts, ParseNode *pn, unsigned flags,
                          unsigned errorNumber, ...);
 
 /*
  * Report a condition that should elicit a warning with JSOPTION_STRICT,
- * or an error if ts or tc is handling strict mode code.  This function
- * defers to ReportCompileErrorNumber to do the real work.  Either tc
- * or ts may be NULL, if there is no tree context or token stream state
- * whose strictness should affect the report.
- *
- * One could have ReportCompileErrorNumber recognize the
- * JSREPORT_STRICT_MODE_ERROR flag instead of having a separate function
- * like this one.  However, the strict mode code flag we need to test is
- * in the ShareContext structure for that code; we would have to change
- * the ~120 ReportCompileErrorNumber calls to pass the additional
- * argument, even though many of those sites would never use it.  Using
- * ts's TSF_STRICT_MODE_CODE flag instead of sc's would be brittle: at some
- * points ts's flags don't correspond to those of the sc relevant to the
- * error.
+ * or an error if the current context is handling strict mode code.
  */
 bool
-ReportStrictModeError(JSContext *cx, TokenStream *ts, SharedContext *sc, ParseNode *pn,
-                      unsigned errorNumber, ...);
+ReportStrictModeError(JSContext *cx, TokenStream *ts, ParseNode *pn, unsigned errorNumber, ...);
 
 } /* namespace js */
 
 extern JS_FRIEND_API(int)
 js_fgets(char *buf, int size, FILE *file);
 
 #ifdef DEBUG
 extern const char *
--- a/js/src/frontend/TreeContext.h
+++ b/js/src/frontend/TreeContext.h
@@ -28,18 +28,18 @@ struct StmtInfo;
 class ContextFlags {
 
     // This class's data is all private and so only visible to these friends.
     friend struct SharedContext;
     friend struct FunctionBox;
 
     // This function/global/eval code body contained a Use Strict Directive.
     // Treat certain strict warnings as errors, and forbid the use of 'with'.
-    // See also TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and
-    // JSREPORT_STRICT_ERROR.
+    // See also StrictModeGetter, JSScript::strictModeCode,
+    // JSREPORT_STRICT_ERROR, and JSOPTION_STRICT_MODE.
     //
     bool            inStrictMode:1;
 
     // The (static) bindings of this script need to support dynamic name
     // read/write access. Here, 'dynamic' means dynamic dictionary lookup on
     // the scope chain for a dynamic set of keys. The primary examples are:
     //  - direct eval
     //  - function::
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1080,19 +1080,26 @@ Function(JSContext *cx, unsigned argc, V
                 return false;
             (void) js_strncpy(cp, arg_chars, arg_length);
             cp += arg_length;
 
             /* Add separating comma or terminating 0. */
             *cp++ = (i + 1 < n) ? ',' : 0;
         }
 
-        /* Initialize a tokenstream that reads from the given string. */
+        /*
+         * Initialize a tokenstream that reads from the given string.  No
+         * StrictModeGetter is needed because this TokenStream won't report any
+         * strict mode errors.  Any strict mode errors which might be reported
+         * here (duplicate argument names, etc.) will be detected when we
+         * compile the function body.
+         */
         TokenStream ts(cx, principals, originPrincipals,
-                       collected_args, args_length, filename, lineno, cx->findVersion());
+                       collected_args, args_length, filename, lineno, cx->findVersion(),
+                       /* strictModeGetter = */ NULL);
 
         /* The argument string may be empty or contain no tokens. */
         TokenKind tt = ts.getToken();
         if (tt != TOK_EOF) {
             for (;;) {
                 /*
                  * Check that it's a name.  This also implicitly guards against
                  * TOK_ERROR, which was already reported.