Bug 678037 - Enable lazy JS parsing and fix various bugs, r=waldo,evilpie,nobody.
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 14 Jun 2013 05:58:28 -0600
changeset 146563 ce43d28276e494e9e3dba477cdcda931d15ffc7c
parent 146562 ccd298a9db28814e7d9bd5b6ae60a80ee9984eb9
child 146564 fbdcb79281aed2ea6f66795f4bfd7e8c229e1f5c
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo, evilpie, nobody
bugs678037
milestone24.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 678037 - Enable lazy JS parsing and fix various bugs, r=waldo,evilpie,nobody.
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/NameFunctions.cpp
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/frontend/SyntaxParseHandler.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/ion/AsmJS.cpp
js/src/ion/BaselineIC.cpp
js/src/ion/CodeGenerator.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/ParallelFunctions.cpp
js/src/jit-test/tests/basic/functionnames.js
js/src/jit-test/tests/basic/lazyparse.js
js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinferinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Debugger.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -69,16 +69,25 @@ CheckArgumentsWithinEval(JSContext *cx, 
     if (script->isGeneratorExp) {
         parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
         return false;
     }
 
     return true;
 }
 
+inline bool
+CanLazilyParse(JSContext *cx, const CompileOptions &options)
+{
+    return options.canLazilyParse &&
+        options.compileAndGo &&
+        options.sourcePolicy == CompileOptions::SAVE_SOURCE &&
+        !cx->compartment()->debugMode();
+}
+
 JSScript *
 frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
                         HandleScript evalCaller,
                         const CompileOptions &options,
                         const jschar *chars, size_t length,
                         JSString *source_ /* = NULL */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionToken *extraSct /* = NULL */)
@@ -97,17 +106,17 @@ frontend::CompileScript(JSContext *cx, H
     if (!CheckLength(cx, length))
         return NULL;
     JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     ScriptSource *ss = cx->new_<ScriptSource>();
     if (!ss)
         return NULL;
     if (options.filename && !ss->setFilename(cx, options.filename))
         return NULL;
-    
+
     JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss));
     if (!sourceObject)
         return NULL;
     SourceCompressionToken mysct(cx);
     SourceCompressionToken *sct = (extraSct) ? extraSct : &mysct;
     switch (options.sourcePolicy) {
       case CompileOptions::SAVE_SOURCE:
         if (!ss->setSourceCopy(cx, chars, length, false, sct))
@@ -115,38 +124,31 @@ frontend::CompileScript(JSContext *cx, H
         break;
       case CompileOptions::LAZY_SOURCE:
         ss->setSourceRetrievable();
         break;
       case CompileOptions::NO_SOURCE:
         break;
     }
 
+    bool canLazilyParse = CanLazilyParse(cx, options);
+
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
-    if (options.canLazilyParse) {
+    if (canLazilyParse) {
         syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) NULL,
                                (LazyScript *) NULL);
     }
 
     Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
-                                    options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
+                                    canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = sct;
 
     GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
 
-    // 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);
-    if (!pc.ref().init())
-        return NULL;
-
     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)
         return NULL;
@@ -164,16 +166,25 @@ frontend::CompileScript(JSContext *cx, H
 
     BytecodeEmitter::EmitterMode emitterMode =
         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller,
                         !!globalScope, options.lineno, emitterMode);
     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);
+    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) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
@@ -308,55 +319,57 @@ frontend::CompileScript(JSContext *cx, H
 
     return script;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
                               const jschar *chars, size_t length)
 {
-    CompileOptions options(cx);
+    JS_ASSERT(cx->compartment() == fun->compartment());
+
+    CompileOptions options(cx, lazy->version());
     options.setPrincipals(cx->compartment()->principals)
-           .setOriginPrincipals(lazy->parent()->originPrincipals)
-           .setVersion(lazy->parent()->getVersion())
-           .setFileAndLine(lazy->parent()->filename(), lazy->lineno())
+           .setOriginPrincipals(lazy->originPrincipals())
+           .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
-           .setCompileAndGo(lazy->parent()->compileAndGo)
+           .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
     Parser<FullParseHandler> parser(cx, options, chars, length,
                                     /* foldConstants = */ true, NULL, lazy);
 
-    RootedObject enclosingScope(cx, lazy->parent()->function());
+    RootedObject enclosingScope(cx, lazy->parentFunction());
 
-    ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->parent()->staticLevel + 1,
-                                                  lazy->strict());
+    ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->staticLevel(), lazy->strict());
     if (!pn)
         return false;
 
-    JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, lazy->source()));
-    if (!sourceObject)
+    if (!NameFunctions(cx, pn))
         return false;
 
+    JS::RootedScriptSource sourceObject(cx, lazy->sourceObject());
+    JS_ASSERT(sourceObject);
+
     Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
-                                                  options, lazy->parent()->staticLevel + 1,
+                                                  options, lazy->staticLevel(),
                                                   sourceObject, lazy->begin(), lazy->end()));
     if (!script)
         return false;
 
     script->bindings = pn->pn_funbox->bindings;
 
     if (lazy->directlyInsideEval())
         script->directlyInsideEval = true;
-
-    bool hasGlobalScope = lazy->parent()->compileAndGo;
+    if (lazy->usesArgumentsAndApply())
+        script->usesArgumentsAndApply = true;
 
     BytecodeEmitter bce(/* parent = */ NULL, &parser, pn->pn_funbox, script, options.forEval,
-                        /* evalCaller = */ NullPtr(), hasGlobalScope,
+                        /* evalCaller = */ NullPtr(), /* hasGlobalScope = */ true,
                         options.lineno, BytecodeEmitter::LazyFunction);
     if (!bce.init())
         return false;
 
     return EmitFunctionScript(cx, &bce, pn->pn_body);
 }
 
 // Compile a JS function body, which might appear as the value of an event
@@ -380,27 +393,29 @@ frontend::CompileFunctionBody(JSContext 
         return false;
     SourceCompressionToken sct(cx);
     JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
+    bool canLazilyParse = CanLazilyParse(cx, options);
+
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
-    if (options.canLazilyParse) {
+    if (canLazilyParse) {
         syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) NULL,
                                (LazyScript *) NULL);
     }
 
     JS_ASSERT(!options.forEval);
 
     Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
-                                    options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
+                                    canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = &sct;
 
     JS_ASSERT(fun);
     JS_ASSERT(fun->isTenured());
 
     fun->setArgCount(formals.length());
 
     /* FIXME: make Function format the source for a function definition. */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1123,21 +1123,32 @@ TryConvertFreeName(BytecodeEmitter *bce,
     }
 
     /*
      * When parsing inner functions lazily, parse nodes for outer functions no
      * longer exist and only the function's scope chain is available for
      * resolving upvar accesses within the inner function.
      */
     if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
+        // The only statements within a lazy function which can push lexical
+        // scopes are try/catch blocks. Use generic ops in this case.
+        for (StmtInfoBCE *stmt = bce->topStmt; stmt; stmt = stmt->down) {
+            switch (stmt->type) {
+              case STMT_TRY:
+              case STMT_FINALLY:
+                return true;
+              default:;
+            }
+        }
+
         size_t hops = 0;
         FunctionBox *funbox = bce->sc->asFunctionBox();
         if (funbox->hasExtensibleScope())
             return false;
-        if (funbox->function()->atom() == pn->pn_atom)
+        if (funbox->function()->isNamedLambda() && funbox->function()->atom() == pn->pn_atom)
             return false;
         if (funbox->function()->isHeavyweight()) {
             hops++;
             if (funbox->function()->isNamedLambda())
                 hops++;
         }
         if (bce->script->directlyInsideEval)
             return false;
@@ -4486,18 +4497,20 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
         (bce->checkSingletonContext() ||
          (!bce->isInLoop() &&
           bce->parent &&
           bce->parent->emittingRunOnceLambda));
     if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
         return false;
 
     if (fun->isInterpretedLazy()) {
-        if (!fun->lazyScript()->parent())
-            fun->lazyScript()->initParent(bce->script);
+        if (!fun->lazyScript()->sourceObject()) {
+            JSFunction *parent = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox()->function() : NULL;
+            fun->lazyScript()->setParent(parent, bce->script->sourceObject(), bce->script->originPrincipals);
+        }
     } else {
         SharedContext *outersc = bce->sc;
 
         if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
             funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
         JS_ASSERT_IF(outersc->strict, funbox->strict);
 
         // Inherit most things (principals, version, etc) from the parent.
@@ -4552,16 +4565,19 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
                                  bce->evalCaller, bce->hasGlobalScope, lineNum,
                                  bce->emitterMode);
             if (!bce2.init())
                 return false;
 
             /* We measured the max scope depth when we parsed the function. */
             if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
                 return false;
+
+            if (funbox->usesArguments && funbox->usesApply)
+                script->usesArgumentsAndApply = true;
         }
     }
 
     /* Make the function object a literal in the outer script's pool. */
     unsigned index = bce->objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     if (!pn->functionIsHoisted())
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -170,26 +170,24 @@ class NameResolver
     /*
      * Resolve the name of a function. If the function already has a name
      * listed, then it is skipped. Otherwise an intelligent name is guessed to
      * assign to the function's displayAtom field
      */
     JSAtom *resolveFun(ParseNode *pn, HandleAtom prefix) {
         JS_ASSERT(pn != NULL && pn->isKind(PNK_FUNCTION));
         RootedFunction fun(cx, pn->pn_funbox->function());
-        if (nparents == 0)
-            return NULL;
 
         StringBuffer buf(cx);
         this->buf = &buf;
 
         /* If the function already has a name, use that */
         if (fun->displayAtom() != NULL) {
             if (prefix == NULL)
-                return fun->atom();
+                return fun->displayAtom();
             if (!buf.append(prefix) ||
                 !buf.append("/") ||
                 !buf.append(fun->displayAtom()))
                 return NULL;
             return buf.finishAtom();
         }
 
         /* If a prefix is specified, then it is a form of namespace */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -414,19 +414,16 @@ Parser<ParseHandler>::Parser(JSContext *
     sct(NULL),
     keepAtoms(cx->runtime()),
     foldConstants(foldConstants),
     compileAndGo(options.compileAndGo),
     selfHostingMode(options.selfHostingMode),
     abortedSyntaxParse(false),
     handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
 {
-    // XXX bug 678037 always disable syntax parsing for now.
-    handler.disableSyntaxParser();
-
     cx->runtime()->activeCompilations++;
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (context->hasExtraWarningsOption())
         handler.disableSyntaxParser();
 
@@ -482,16 +479,18 @@ FunctionBox::FunctionBox(JSContext *cx, 
     bufStart(0),
     bufEnd(0),
     asmStart(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     useAsm(false),
     insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
+    usesArguments(false),
+    usesApply(false),
     funCxFlags()
 {
     JS_ASSERT(fun->isTenured());
 
     if (!outerpc) {
         inWith = false;
 
     } else if (outerpc->parsingWith) {
@@ -920,16 +919,17 @@ Parser<FullParseHandler>::checkFunctionA
      */
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
         if (r.front().key() == arguments) {
             Definition *dn = r.front().value().get<FullParseHandler>();
             pc->lexdeps->remove(arguments);
             dn->pn_dflags |= PND_IMPLICITARGUMENTS;
             if (!pc->define(context, arguments, dn, Definition::VAR))
                 return false;
+            pc->sc->asFunctionBox()->usesArguments = true;
             break;
         }
     }
 
     /*
      * Report error if both rest parameters and 'arguments' are used. Do this
      * check before adding artificial 'arguments' below.
      */
@@ -1009,21 +1009,25 @@ Parser<FullParseHandler>::checkFunctionA
 
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkFunctionArguments()
 {
-    if (pc->sc->asFunctionBox()->function()->hasRest()) {
-        if (pc->lexdeps->lookup(context->names().arguments)) {
+    bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
+
+    if (pc->lexdeps->lookup(context->names().arguments)) {
+        pc->sc->asFunctionBox()->usesArguments = true;
+        if (hasRest) {
             report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
             return false;
         }
+    } else if (hasRest) {
         DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
         if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
             report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
             return false;
         }
     }
 
     return true;
@@ -1336,24 +1340,16 @@ Parser<FullParseHandler>::leaveFunction(
             JS_ASSERT(dn->isPlaceholder());
 
             if (atom == funName && kind == Expression) {
                 if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
                     return false;
                 continue;
             }
 
-            /*
-             * If there are no uses of this placeholder (e.g., it was created
-             * for an identifierName that turned out to be a label), there is
-             * nothing left to do.
-             */
-            if (!dn->dn_uses)
-                continue;
-
             Definition *outer_dn = outerpc->decls().lookupFirst(atom);
 
             /*
              * Make sure to deoptimize lexical dependencies that are polluted
              * by eval and function statements (which both flag the function as
              * having an extensible scope) or any enclosing 'with'.
              */
             if (funbox->hasExtensibleScope() || outerpc->parsingWith)
@@ -2041,17 +2037,17 @@ Parser<SyntaxParseHandler>::finishFuncti
     // still available.
 
     if (funbox->inWith)
         return abortIfSyntaxParser();
 
     size_t numFreeVariables = pc->lexdeps->count();
     size_t numInnerFunctions = pc->innerFunctions.length();
 
-    LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions,
+    LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions, versionNumber(),
                                           funbox->bufStart, funbox->bufEnd,
                                           funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     HeapPtrAtom *freeVariables = lazy->freeVariables();
     size_t i = 0;
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
@@ -2059,16 +2055,18 @@ Parser<SyntaxParseHandler>::finishFuncti
     JS_ASSERT(i == numFreeVariables);
 
     HeapPtrFunction *innerFunctions = lazy->innerFunctions();
     for (size_t i = 0; i < numInnerFunctions; i++)
         innerFunctions[i].init(pc->innerFunctions[i]);
 
     if (pc->sc->strict)
         lazy->setStrict();
+    if (funbox->usesArguments && funbox->usesApply)
+        lazy->setUsesArgumentsAndApply();
     PropagateTransitiveParseFlags(funbox, lazy);
 
     funbox->object->toFunction()->initLazyScript(lazy);
     return true;
 }
 
 template <>
 bool
@@ -2168,16 +2166,21 @@ Parser<SyntaxParseHandler>::functionArgs
                                                 size_t startOffset, FunctionType type,
                                                 FunctionSyntaxKind kind,
                                                 bool strict, bool *becameStrict)
 {
     if (becameStrict)
         *becameStrict = false;
     ParseContext<SyntaxParseHandler> *outerpc = pc;
 
+    // As from a full parse handler, abort if functions are defined within
+    // lexical scopes.
+    if (pc->topScopeStmt)
+        return abortIfSyntaxParser();
+
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(fun, pc, strict);
     if (!funbox)
         return false;
 
     // Initialize early for possible flags mutation via destructuringExpr.
     ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
                                            outerpc->staticLevel + 1, outerpc->blockidGen);
@@ -4094,16 +4097,17 @@ Parser<SyntaxParseHandler>::forStatement
     bool forOf;
     if (lhsNode && matchInOrOf(&forOf)) {
         /* Parse the rest of the for/in or for/of head. */
         forStmt.type = STMT_FOR_IN_LOOP;
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         if (!forDecl &&
             lhsNode != SyntaxParseHandler::NodeName &&
+            lhsNode != SyntaxParseHandler::NodeGetProp &&
             lhsNode != SyntaxParseHandler::NodeLValue)
         {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
@@ -4306,22 +4310,25 @@ Parser<ParseHandler>::tryStatement()
     if (!pn)
         return null();
 
     handler.setBeginPosition(pn, begin);
     handler.setEndPosition(pn, finallyBlock ? finallyBlock : catchList);
     return pn;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::withStatement()
-{
-    if (!abortIfSyntaxParser())
+template <>
+ParseNode *
+Parser<FullParseHandler>::withStatement()
+{
+    if (handler.syntaxParser) {
+        handler.disableSyntaxParser();
+        abortedSyntaxParse = true;
         return null();
+    }
 
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
     uint32_t begin = tokenStream.currentToken().pos.begin;
 
     // In most cases, we want the constructs forbidden in strict mode code to be
     // a subset of those that JSOPTION_EXTRA_WARNINGS warns about, and we should
     // use reportStrictModeError.  However, 'with' is the sole instance of a
     // construct that is forbidden in strict mode code, but doesn't even merit a
@@ -4349,31 +4356,39 @@ Parser<ParseHandler>::withStatement()
     pc->sc->setBindingsAccessedDynamically();
     pc->parsingWith = oldParsingWith;
 
     /*
      * Make sure to deoptimize lexical dependencies inside the |with|
      * to safely optimize binding globals (see bug 561923).
      */
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
-        DefinitionNode defn = r.front().value().get<ParseHandler>();
+        DefinitionNode defn = r.front().value().get<FullParseHandler>();
         DefinitionNode lexdep = handler.resolve(defn);
         handler.deoptimizeUsesWithin(lexdep,
                                      TokenPos::make(begin, tokenStream.currentToken().pos.begin));
     }
 
     Node pn = handler.newBinary(PNK_WITH, objectExpr, innerBlock);
     if (!pn)
         return null();
 
     handler.setBeginPosition(pn, begin);
     handler.setEndPosition(pn, innerBlock);
     return pn;
 }
 
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::withStatement()
+{
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
+    return null();
+}
+
 #if JS_HAS_BLOCK_SCOPE
 template <>
 ParseNode *
 Parser<FullParseHandler>::letStatement()
 {
     handler.disableSyntaxParser();
 
     ParseNode *pn;
@@ -5230,18 +5245,22 @@ Parser<FullParseHandler>::setAssignmentL
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op)
 {
     /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
-    if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue)
+    if (pn != SyntaxParseHandler::NodeName &&
+        pn != SyntaxParseHandler::NodeGetProp &&
+        pn != SyntaxParseHandler::NodeLValue)
+    {
         return abortIfSyntaxParser();
+    }
     return checkStrictAssignment(pn);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr()
 {
     JS_CHECK_RECURSION(context, return null());
@@ -5414,18 +5433,22 @@ bool
 Parser<SyntaxParseHandler>::checkDeleteExpression(Node *pn)
 {
     PropertyName *name = handler.isName(*pn);
     if (name)
         return report(ParseStrictError, pc->sc->strict, *pn, JSMSG_DEPRECATED_DELETE_OPERAND);
 
     // Treat deletion of non-lvalues as ambiguous, so that any error associated
     // with deleting a call expression is reported.
-    if (*pn != SyntaxParseHandler::NodeLValue && strictMode())
+    if (*pn != SyntaxParseHandler::NodeGetProp &&
+        *pn != SyntaxParseHandler::NodeLValue &&
+        strictMode())
+    {
         return abortIfSyntaxParser();
+    }
 
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr()
 {
@@ -6439,20 +6462,23 @@ Parser<ParseHandler>::memberExpr(TokenKi
                      * In non-strict mode code, direct calls to eval can add
                      * variables to the call object.
                      */
                     if (pc->sc->isFunctionBox() && !pc->sc->strict)
                         pc->sc->asFunctionBox()->setHasExtensibleScope();
                 }
             } else if (JSAtom *atom = handler.isGetProp(lhs)) {
                 /* Select JSOP_FUNAPPLY given foo.apply(...). */
-                if (atom == context->names().apply)
+                if (atom == context->names().apply) {
                     handler.setOp(nextMember, JSOP_FUNAPPLY);
-                else if (atom == context->names().call)
+                    if (pc->sc->isFunctionBox())
+                        pc->sc->asFunctionBox()->usesApply = true;
+                } else if (atom == context->names().call) {
                     handler.setOp(nextMember, JSOP_FUNCALL);
+                }
             }
 
             handler.setBeginPosition(nextMember, lhs);
             handler.addList(nextMember, lhs);
 
             if (!argumentList(nextMember))
                 return null();
         } else {
@@ -6546,17 +6572,27 @@ Parser<FullParseHandler>::newRegExp(cons
     pn->setOp(JSOP_REGEXP);
     return pn;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::newRegExp(const jschar *buf, size_t length, RegExpFlag flags)
 {
-    return SyntaxParseHandler::NodeGeneric;
+    // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
+    const StableCharPtr chars(buf, length);
+    RegExpStatics *res = context->regExpStatics();
+
+    RegExpObject *reobj;
+    if (context->hasfp())
+        reobj = RegExpObject::create(context, res, chars.get(), length, flags, &tokenStream);
+    else
+        reobj = RegExpObject::createNoStatics(context, chars.get(), length, flags, &tokenStream);
+
+    return reobj ? SyntaxParseHandler::NodeGeneric : SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::primaryExpr(TokenKind tt)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
 
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -205,16 +205,20 @@ class FunctionBox : public ObjectBox, pu
     uint32_t        startColumn;
     uint32_t        asmStart;               /* offset of the "use asm" directive, if present */
     uint16_t        ndefaults;
     bool            inWith:1;               /* some enclosing scope is a with-statement */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
     bool            useAsm:1;               /* function contains "use asm" directive */
     bool            insideUseAsm:1;         /* nested function of function of "use asm" directive */
 
+    // 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(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext<ParseHandler> *pc,
                 bool strict);
 
     ObjectBox *toObjectBox() { return this; }
     JSFunction *function() const { return object->toFunction(); }
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -27,16 +27,17 @@ class SyntaxParseHandler
     TokenPos lastStringPos;
     TokenStream &tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
         NodeName,
+        NodeGetProp,
         NodeString,
         NodeStringExprStatement,
         NodeLValue
     };
     typedef Definition::Kind DefinitionNode;
 
     SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
                        Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
@@ -97,17 +98,21 @@ class SyntaxParseHandler
 
     Node newBreak(PropertyName *label, uint32_t begin, uint32_t end) {
         return NodeGeneric;
     }
     Node newContinue(PropertyName *label, uint32_t begin, uint32_t end) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos &pos) { return NodeGeneric; }
-    Node newPropertyAccess(Node pn, PropertyName *name, uint32_t end) { return NodeLValue; }
+    Node newPropertyAccess(Node pn, PropertyName *name, uint32_t end)
+    {
+        lastAtom = name;
+        return NodeGetProp;
+    }
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; }
 
     bool addCatchBlock(Node catchList, Node letBlock,
                        Node catchName, Node catchGuard, Node catchBody) { return true; }
 
     void morphNameIntoLabel(Node name, Node statement) {}
     void setLeaveBlockResult(Node block, Node kid, bool leaveBlockExpr) {}
 
@@ -154,17 +159,19 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
     PropertyName *isName(Node pn) {
         return (pn == NodeName) ? lastAtom->asPropertyName() : NULL;
     }
-    PropertyName *isGetProp(Node pn) { return NULL; }
+    PropertyName *isGetProp(Node pn) {
+        return (pn == NodeGetProp) ? lastAtom->asPropertyName() : NULL;
+    }
     JSAtom *isStringExprStatement(Node pn, TokenPos *pos) {
         if (pn == NodeStringExprStatement) {
             *pos = lastStringPos;
             return lastAtom;
         }
         return NULL;
     }
     bool isEmptySemicolon(Node pn) { return false; }
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -552,46 +552,47 @@ TokenStream::advance(size_t position)
     cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
     cur->type = TOK_ERROR;
     lookahead = 0;
 }
 
 void
 TokenStream::tell(Position *pos)
 {
-    pos->buf = userbuf.addressOfNextRawChar();
+    pos->buf = userbuf.addressOfNextRawChar(/* allowPoisoned = */ true);
     pos->flags = flags;
     pos->lineno = lineno;
     pos->linebase = linebase;
     pos->prevLinebase = prevLinebase;
     pos->lookahead = lookahead;
     pos->currentToken = currentToken();
     for (unsigned i = 0; i < lookahead; i++)
         pos->lookaheadTokens[i] = tokens[(cursor + 1 + i) & ntokensMask];
 }
 
 void
 TokenStream::seek(const Position &pos)
 {
-    userbuf.setAddressOfNextRawChar(pos.buf);
+    userbuf.setAddressOfNextRawChar(pos.buf, /* allowPoisoned = */ true);
     flags = pos.flags;
     lineno = pos.lineno;
     linebase = pos.linebase;
     prevLinebase = pos.prevLinebase;
     lookahead = pos.lookahead;
 
     tokens[cursor] = pos.currentToken;
     for (unsigned i = 0; i < lookahead; i++)
         tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
 }
 
 void
 TokenStream::seek(const Position &pos, const TokenStream &other)
 {
     srcCoords.fill(other.srcCoords);
+    lastFunctionKeyword = other.lastFunctionKeyword;
     seek(pos);
 }
 
 void
 TokenStream::positionAfterLastFunctionKeyword(Position &pos)
 {
     JS_ASSERT(lastFunctionKeyword.buf > userbuf.base());
     PodAssign(&pos, &lastFunctionKeyword);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -827,24 +827,24 @@ class MOZ_STACK_CLASS TokenStream
             return false;
         }
 
         void ungetRawChar() {
             JS_ASSERT(ptr);     /* make sure haven't been poisoned */
             ptr--;
         }
 
-        const jschar *addressOfNextRawChar() const {
-            JS_ASSERT(ptr);     /* make sure haven't been poisoned */
+        const jschar *addressOfNextRawChar(bool allowPoisoned = false) const {
+            JS_ASSERT_IF(!allowPoisoned, ptr);     /* make sure haven't been poisoned */
             return ptr;
         }
 
         /* Use this with caution! */
-        void setAddressOfNextRawChar(const jschar *a) {
-            JS_ASSERT(a);
+        void setAddressOfNextRawChar(const jschar *a, bool allowPoisoned = false) {
+            JS_ASSERT_IF(!allowPoisoned, a);
             ptr = a;
         }
 
 #ifdef DEBUG
         /* Poison the TokenBuf so it cannot be accessed again. */
         void poison() {
             ptr = NULL;
         }
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -6259,17 +6259,17 @@ js::IsAsmJSFunction(JSContext *cx, unsig
 AsmJSModule::~AsmJSModule()
 {
     if (code_) {
         for (unsigned i = 0; i < numExits(); i++) {
             AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
             if (!exitDatum.fun)
                 continue;
 
-            if (!exitDatum.fun->isInterpreted())
+            if (!exitDatum.fun->hasScript())
                 continue;
 
             JSScript *script = exitDatum.fun->nonLazyScript();
             if (!script->hasIonScript())
                 continue;
 
             DependentAsmJSModuleExit exit(this, i);
             script->ionScript()->removeDependentAsmJSModule(exit);
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -1319,16 +1319,19 @@ ICUpdatedStub::addUpdateStubForValue(JSC
                                      jsid id, HandleValue val)
 {
     if (numOptimizedStubs_ >= MAX_OPTIMIZED_STUBS) {
         // TODO: if the TypeSet becomes unknown or has the AnyObject type,
         // replace stubs with a single stub to handle these.
         return true;
     }
 
+    if (!obj->getType(cx))
+        return false;
+
     types::EnsureTrackPropertyTypes(cx, obj, id);
 
     if (val.isPrimitive()) {
         JSValueType type = val.isDouble() ? JSVAL_TYPE_DOUBLE : val.extractNonDoubleType();
 
         // Check for existing TypeUpdate stub.
         ICTypeUpdate_PrimitiveSet *existingStub = NULL;
         for (ICStubConstIterator iter = firstUpdateStub_; !iter.atEnd(); iter++) {
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1689,16 +1689,20 @@ CodeGenerator::visitCallKnown(LCallKnown
             masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
             masm.bind(&notPrimitive);
         }
 
         dropArguments(call->numStackArgs() + 1);
         return true;
     }
 
+    // The calleereg is known to be a non-native function, but might point to
+    // a LazyScript instead of a JSScript.
+    masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
+
     // Knowing that calleereg is a non-native function, load the JSScript.
     masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
     if (call->mir()->needsArgCheck())
         masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
     else
         masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -195,16 +195,18 @@ IonBuilder::getPolyCallTargets(types::St
             JS_ASSERT(appendOk);
         } else {
             types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
             JS_ASSERT(typeObj);
             if (!typeObj->isFunction() || !typeObj->interpretedFunction) {
                 targets.clear();
                 return true;
             }
+            if (!typeObj->interpretedFunction->getOrCreateScript(cx))
+                return false;
             DebugOnly<bool> appendOk = targets.append(typeObj->interpretedFunction);
             JS_ASSERT(appendOk);
 
             *gotLambda = true;
         }
     }
 
     // For now, only inline "singleton" lambda calls
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -851,24 +851,28 @@ GetPossibleCallees(JSContext *cx,
             rootedFun = typeObj->interpretedFunction;
             if (!rootedFun)
                 continue;
         }
 
         if (!rootedFun->isInterpreted())
             continue;
 
-        if (rootedFun->nonLazyScript()->shouldCloneAtCallsite) {
+        rootedScript = rootedFun->getOrCreateScript(cx);
+        if (!rootedScript)
+            return false;
+
+        if (rootedScript->shouldCloneAtCallsite) {
             rootedFun = CloneFunctionAtCallsite(cx, rootedFun, script, pc);
             if (!rootedFun)
                 return false;
+            rootedScript = rootedFun->nonLazyScript();
         }
 
         // check if this call target is already known
-        rootedScript = rootedFun->nonLazyScript();
         if (!AddCallTarget(rootedScript, targets))
             return false;
     }
 
     return true;
 }
 
 static bool
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -429,16 +429,18 @@ ion::ParCallToUncompiledScript(JSFunctio
 
     JS_ASSERT(InParallelSection());
 
 #ifdef DEBUG
     if (func->hasScript()) {
         JSScript *script = func->nonLazyScript();
         Spew(SpewBailouts, "Call to uncompiled script: %p:%s:%d",
              script, script->filename(), script->lineno);
+    } else if (func->isInterpretedLazy()) {
+        Spew(SpewBailouts, "Call to uncompiled lazy script");
     } else if (func->isBoundFunction()) {
         int depth = 0;
         JSFunction *target = func->getBoundFunctionTarget()->toFunction();
         while (depth < max_bound_function_unrolling) {
             if (target->hasScript())
                 break;
             if (target->isBoundFunction())
                 target = target->getBoundFunctionTarget()->toFunction();
--- a/js/src/jit-test/tests/basic/functionnames.js
+++ b/js/src/jit-test/tests/basic/functionnames.js
@@ -31,17 +31,17 @@ var Baz = Bar = function(){}
 assertName(Baz, 'Bar');
 assertName(Bar, 'Bar');
 
 /* returned from an immediate function */
 var Foo = function (){
     assertName(arguments.callee, 'Foo<')
     return function(){};
 }();
-assertName(Foo, 'Foo');
+assertName(Foo, 'Foo</<');
 
 /* various properties and such */
 var x = {fox: { bax: function(){} } };
 assertName(x.fox.bax, 'x.fox.bax');
 var foo = {foo: {foo: {}}};
 foo.foo.foo = function(){};
 assertName(foo.foo.foo, 'foo.foo.foo');
 var z = {
@@ -89,17 +89,17 @@ a.b();
 var bar = 'bar';
 a.b[bar] = function(){};
 assertName(a.b.bar, 'a.b[bar]');
 
 a.b = function() {
     assertName(arguments.callee, 'a.b<');
     return { a: function() {} }
 }();
-assertName(a.b.a, 'a.b.a');
+assertName(a.b.a, 'a.b</<.a');
 
 a = {
     b: function(a) {
         if (a)
             return function() {};
         else
             return function() {};
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/lazyparse.js
@@ -0,0 +1,36 @@
+
+function outer() {
+    var xyz = 0;
+    function foo() {
+	function bar() { xyz++; }
+	bar();
+	let x = 3;
+    }
+    foo();
+    assertEq(xyz, 1);
+}
+outer();
+
+function mapfloor(a) {
+  var b = a.map(function(v) {
+        "use strict";
+        try {
+            eval("delete String;");
+        } catch (e) {
+            return e instanceof res;
+        }
+    });
+  var res = "";
+}
+try {
+    mapfloor([1,2]);
+} catch (e) {}
+
+test();
+function test() {
+  try  { 
+    eval('let(z) { with({}) let y = 3; }');
+  } catch(ex) {
+    (function(x) { return !(x) })(0/0)
+  }
+}
--- a/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
+++ b/js/src/jit-test/tests/basic/testOOMInAutoEnterCompartment.js
@@ -1,12 +1,15 @@
-foo = evalcx("(function foo() { foo.bar() })");
-foo.bar = evalcx("(function bar() {})");
 
 function fatty() {
     try {
         fatty();
     } catch (e) {
         foo();
     }
 }
 
-fatty();
+if (!getBuildConfiguration()['root-analysis']) { // >:(
+    foo = evalcx("(function foo() { foo.bar() })");
+    foo.bar = evalcx("(function bar() {})");
+
+    fatty();
+}
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5284,20 +5284,20 @@ AutoFile::open(JSContext *cx, const char
                                  filename, "No such file or directory");
             return false;
         }
     }
     return true;
 }
 
 
-JS::CompileOptions::CompileOptions(JSContext *cx)
+JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
     : principals(NULL),
       originPrincipals(NULL),
-      version(cx->findVersion()),
+      version(version != JSVERSION_UNKNOWN ? version : cx->findVersion()),
       versionSet(false),
       utf8(false),
       filename(NULL),
       lineno(1),
       column(0),
       element(NullPtr()),
       compileAndGo(cx->hasOption(JSOPTION_COMPILE_N_GO)),
       forEval(false),
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3902,17 +3902,17 @@ struct JS_PUBLIC_API(CompileOptions) {
     bool selfHostingMode;
     bool canLazilyParse;
     enum SourcePolicy {
         NO_SOURCE,
         LAZY_SOURCE,
         SAVE_SOURCE
     } sourcePolicy;
 
-    explicit CompileOptions(JSContext *cx);
+    explicit CompileOptions(JSContext *cx, JSVersion version = JSVERSION_UNKNOWN);
     CompileOptions &setPrincipals(JSPrincipals *p) { principals = p; return *this; }
     CompileOptions &setOriginPrincipals(JSPrincipals *p) { originPrincipals = p; return *this; }
     CompileOptions &setVersion(JSVersion v) { version = v; versionSet = true; return *this; }
     CompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions &setFileAndLine(const char *f, unsigned l) {
         filename = f; lineno = l; return *this;
     }
     CompileOptions &setColumn(unsigned c) { column = c; return *this; }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -598,16 +598,87 @@ JSCompartment::hasScriptsOnStack()
     for (ActivationIterator iter(rt); !iter.done(); ++iter) {
         if (iter.activation()->compartment() == this)
             return true;
     }
 
     return false;
 }
 
+static bool
+AddInnerLazyFunctionsFromScript(JSScript *script, AutoObjectVector &lazyFunctions)
+{
+    if (!script->hasObjects())
+        return true;
+    ObjectArray *objects = script->objects();
+    for (size_t i = script->innerObjectsStart(); i < objects->length; i++) {
+        JSObject *obj = objects->vector[i];
+        if (obj->isFunction() && obj->toFunction()->isInterpretedLazy()) {
+            if (!lazyFunctions.append(obj))
+                return false;
+        }
+    }
+    return true;
+}
+
+static bool
+CreateLazyScriptsForCompartment(JSContext *cx)
+{
+    AutoObjectVector lazyFunctions(cx);
+
+    // Find all root lazy functions in the compartment: those which have not been
+    // compiled and which have a source object, indicating that their parent has
+    // been compiled.
+    for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) {
+        JSObject *obj = i.get<JSObject>();
+        if (obj->compartment() == cx->compartment() && obj->isFunction()) {
+            JSFunction *fun = obj->toFunction();
+            if (fun->isInterpretedLazy()) {
+                LazyScript *lazy = fun->lazyScript();
+                if (lazy->sourceObject() && !lazy->maybeScript()) {
+                    if (!lazyFunctions.append(fun))
+                        return false;
+                }
+            }
+        }
+    }
+
+    // Create scripts for each lazy function, updating the list of functions to
+    // process with any newly exposed inner functions in created scripts.
+    // A function cannot be delazified until its outer script exists.
+    for (size_t i = 0; i < lazyFunctions.length(); i++) {
+        JSFunction *fun = lazyFunctions[i]->toFunction();
+
+        // lazyFunctions may have been populated with multiple functions for
+        // a lazy script.
+        if (!fun->isInterpretedLazy())
+            continue;
+
+        JSScript *script = fun->getOrCreateScript(cx);
+        if (!script)
+            return false;
+        if (!AddInnerLazyFunctionsFromScript(script, lazyFunctions))
+            return false;
+    }
+
+    // Repoint any clones of the original functions to their new script.
+    for (gc::CellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) {
+        JSObject *obj = i.get<JSObject>();
+        if (obj->compartment() == cx->compartment() && obj->isFunction()) {
+            JSFunction *fun = obj->toFunction();
+            if (fun->isInterpretedLazy()) {
+                JS_ASSERT(fun->lazyScript()->maybeScript());
+                JS_ALWAYS_TRUE(fun->getOrCreateScript(cx));
+            }
+        }
+    }
+
+    return true;
+}
+
 bool
 JSCompartment::setDebugModeFromC(JSContext *cx, bool b, AutoDebugModeGC &dmgc)
 {
     bool enabledBefore = debugMode();
     bool enabledAfter = (debugModeBits & ~unsigned(DebugFromC)) || b;
 
     // Debug mode can be enabled only when no scripts from the target
     // compartment are on the stack. It would even be incorrect to discard just
@@ -621,16 +692,18 @@ JSCompartment::setDebugModeFromC(JSConte
     //
     bool onStack = false;
     if (enabledBefore != enabledAfter) {
         onStack = hasScriptsOnStack();
         if (b && onStack) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
             return false;
         }
+        if (enabledAfter && !CreateLazyScriptsForCompartment(cx))
+            return false;
     }
 
     debugModeBits = (debugModeBits & ~unsigned(DebugFromC)) | (b ? DebugFromC : 0);
     JS_ASSERT(debugMode() == enabledAfter);
     if (enabledBefore != enabledAfter) {
         updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
         if (!enabledAfter)
             DebugScopes::onCompartmentLeaveDebugMode(this);
@@ -672,20 +745,24 @@ bool
 JSCompartment::addDebuggee(JSContext *cx, js::GlobalObject *global)
 {
     AutoDebugModeGC dmgc(cx->runtime());
     return addDebuggee(cx, global, dmgc);
 }
 
 bool
 JSCompartment::addDebuggee(JSContext *cx,
-                           js::GlobalObject *global,
+                           GlobalObject *globalArg,
                            AutoDebugModeGC &dmgc)
 {
+    Rooted<GlobalObject*> global(cx, globalArg);
+
     bool wasEnabled = debugMode();
+    if (!wasEnabled && !CreateLazyScriptsForCompartment(cx))
+        return false;
     if (!debuggees.put(global)) {
         js_ReportOutOfMemory(cx);
         return false;
     }
     debugModeBits |= DebugFromJS;
     if (!wasEnabled) {
         updateForDebugMode(cx->runtime()->defaultFreeOp(), dmgc);
     }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -345,19 +345,21 @@ js::XDRInterpretedFunction(XDRState<mode
             JSAutoByteString funNameBytes;
             if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
                                      name);
             }
             return false;
         }
         firstword = !!fun->atom();
-        flagsword = (fun->nargs << 16) | fun->flags;
+        script = fun->getOrCreateScript(cx);
+        if (!script)
+            return false;
         atom = fun->atom();
-        script = fun->nonLazyScript();
+        flagsword = (fun->nargs << 16) | fun->flags;
     } else {
         fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(),
                           JSFunction::FinalizeKind, TenuredObject);
         if (!fun)
             return false;
         atom = NULL;
         script = NULL;
     }
@@ -1052,45 +1054,46 @@ JSFunction::createScriptForLazilyInterpr
 {
     JS_ASSERT(fun->isInterpretedLazy());
 
     if (LazyScript *lazy = fun->lazyScriptOrNull()) {
         /* Trigger a pre barrier on the lazy script being overwritten. */
         if (cx->zone()->needsBarrier())
             LazyScript::writeBarrierPre(lazy);
 
+        fun->flags &= ~INTERPRETED_LAZY;
+        fun->flags |= INTERPRETED;
+
         if (JSScript *script = lazy->maybeScript()) {
-            fun->flags &= ~INTERPRETED_LAZY;
-            fun->flags |= INTERPRETED;
             fun->initScript(script);
 
             /*
              * Set some bits that are normally filled in by the Parser after
              * the full parse tree has been produced.
              */
             if (script->function()->isHeavyweight())
                 fun->setIsHeavyweight();
             fun->nargs = script->function()->nargs;
             return true;
         }
 
-        /* Lazily parsed script. */
-        const jschar *chars = lazy->source()->chars(cx);
-        if (!chars)
-            return false;
+        fun->initScript(NULL);
+
+        JS_ASSERT(lazy->source()->hasSourceData());
 
         /*
          * GC must be suppressed for the remainder of the lazy parse, as any
          * GC activity may destroy the characters.
          */
         AutoSuppressGC suppressGC(cx);
 
-        fun->flags &= ~INTERPRETED_LAZY;
-        fun->flags |= INTERPRETED;
-        fun->initScript(NULL);
+        /* Lazily parsed script. */
+        const jschar *chars = lazy->source()->chars(cx);
+        if (!chars)
+            return false;
 
         const jschar *lazyStart = chars + lazy->begin();
         size_t lazyLength = lazy->end() - lazy->begin();
 
         if (!frontend::CompileLazyFunction(cx, fun, lazy, lazyStart, lazyLength)) {
             fun->initLazyScript(lazy);
             return false;
         }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -137,17 +137,16 @@ class JSFunction : public JSObject
         return isLambda() && atom_ && !hasGuessedAtom();
     }
 
     /* Returns the strictness of this function, which must be interpreted. */
     inline bool strict() const;
 
     // Can be called multiple times by the parser.
     void setArgCount(uint16_t nargs) {
-        JS_ASSERT(this->nargs == 0 || this->nargs == nargs);
         this->nargs = nargs;
     }
 
     // Can be called multiple times by the parser.
     void setHasRest() {
         flags |= HAS_REST;
     }
 
@@ -181,23 +180,16 @@ class JSFunction : public JSObject
         flags |= HEAVYWEIGHT;
     }
 
     // Can be called multiple times by the parser.
     void setIsExprClosure() {
         flags |= EXPR_CLOSURE;
     }
 
-    void markNotLazy() {
-        JS_ASSERT(isInterpretedLazy());
-        JS_ASSERT(hasScript());
-        flags |= INTERPRETED;
-        flags &= ~INTERPRETED_LAZY;
-    }
-
     JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); }
     js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? NULL : atom_->asPropertyName(); }
     inline void initAtom(JSAtom *atom);
     JSAtom *displayAtom() const { return atom_; }
 
     inline void setGuessedAtom(JSAtom *atom);
 
     /* uint16_t representation bounds number of call object dynamic slots. */
@@ -216,17 +208,16 @@ class JSFunction : public JSObject
 
     static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
 
     JSScript *getOrCreateScript(JSContext *cx) {
         JS_ASSERT(isInterpreted());
         JS_ASSERT(cx);
         if (isInterpretedLazy()) {
             JS::RootedFunction self(cx, this);
-            js::MaybeCheckStackRoots(cx);
             if (!createScriptForLazilyInterpretedFunction(cx, self))
                 return NULL;
             JS_ASSERT(self->hasScript());
             return self->u.i.s.script_;
         }
         JS_ASSERT(hasScript());
         return u.i.s.script_;
     }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -163,17 +163,18 @@ const uint32_t Arena::FirstThingOffsets[
  * Finalization order for incrementally swept things.
  */
 
 static const AllocKind FinalizePhaseStrings[] = {
     FINALIZE_EXTERNAL_STRING
 };
 
 static const AllocKind FinalizePhaseScripts[] = {
-    FINALIZE_SCRIPT
+    FINALIZE_SCRIPT,
+    FINALIZE_LAZY_SCRIPT
 };
 
 static const AllocKind FinalizePhaseIonCode[] = {
     FINALIZE_IONCODE
 };
 
 static const AllocKind* FinalizePhases[] = {
     FinalizePhaseStrings,
@@ -208,17 +209,16 @@ static const AllocKind BackgroundPhaseOb
 };
 
 static const AllocKind BackgroundPhaseStrings[] = {
     FINALIZE_SHORT_STRING,
     FINALIZE_STRING
 };
 
 static const AllocKind BackgroundPhaseShapes[] = {
-    FINALIZE_LAZY_SCRIPT,
     FINALIZE_SHAPE,
     FINALIZE_BASE_SHAPE,
     FINALIZE_TYPE_OBJECT
 };
 
 static const AllocKind* BackgroundPhases[] = {
     BackgroundPhaseObjects,
     BackgroundPhaseStrings,
@@ -1435,17 +1435,17 @@ ArenaLists::queueStringsForSweep(FreeOp 
     queueForForegroundSweep(fop, FINALIZE_EXTERNAL_STRING);
 }
 
 void
 ArenaLists::queueScriptsForSweep(FreeOp *fop)
 {
     gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_SCRIPT);
     queueForForegroundSweep(fop, FINALIZE_SCRIPT);
-    queueForBackgroundSweep(fop, FINALIZE_LAZY_SCRIPT);
+    queueForForegroundSweep(fop, FINALIZE_LAZY_SCRIPT);
 }
 
 void
 ArenaLists::queueIonCodeForSweep(FreeOp *fop)
 {
     gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_IONCODE);
     queueForForegroundSweep(fop, FINALIZE_IONCODE);
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -199,17 +199,17 @@ IsBackgroundFinalized(AllocKind kind)
         true,      /* FINALIZE_OBJECT4_BACKGROUND */
         false,     /* FINALIZE_OBJECT8 */
         true,      /* FINALIZE_OBJECT8_BACKGROUND */
         false,     /* FINALIZE_OBJECT12 */
         true,      /* FINALIZE_OBJECT12_BACKGROUND */
         false,     /* FINALIZE_OBJECT16 */
         true,      /* FINALIZE_OBJECT16_BACKGROUND */
         false,     /* FINALIZE_SCRIPT */
-        true,      /* FINALIZE_LAZY_SCRIPT */
+        false,     /* FINALIZE_LAZY_SCRIPT */
         true,      /* FINALIZE_SHAPE */
         true,      /* FINALIZE_BASE_SHAPE */
         true,      /* FINALIZE_TYPE_OBJECT */
         true,      /* FINALIZE_SHORT_STRING */
         true,      /* FINALIZE_STRING */
         false,     /* FINALIZE_EXTERNAL_STRING */
         false,     /* FINALIZE_IONCODE */
     };
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -729,38 +729,30 @@ UseNewTypeForClone(JSFunction *fun)
      *
      * Each instance of the innermost function will have a different wrapped
      * initialize method. We capture this, along with similar cases, by looking
      * for short scripts which use both .apply and arguments. For such scripts,
      * whenever creating a new instance of the function we both give that
      * instance a singleton type and clone the underlying script.
      */
 
-    JSScript *script = fun->nonLazyScript();
-
-    if (script->length >= 50)
-        return false;
-
-    if (script->hasConsts() || script->hasObjects() || script->hasRegexps() || fun->isHeavyweight())
-        return false;
-
-    bool hasArguments = false;
-    bool hasApply = false;
-
-    for (jsbytecode *pc = script->code;
-         pc != script->code + script->length;
-         pc += GetBytecodeLength(pc))
-    {
-        if (*pc == JSOP_ARGUMENTS)
-            hasArguments = true;
-        if (*pc == JSOP_FUNAPPLY)
-            hasApply = true;
+    uint32_t begin, end;
+    if (fun->hasScript()) {
+        if (!fun->nonLazyScript()->usesArgumentsAndApply)
+            return false;
+        begin = fun->nonLazyScript()->sourceStart;
+        end = fun->nonLazyScript()->sourceEnd;
+    } else {
+        if (!fun->lazyScript()->usesArgumentsAndApply())
+            return false;
+        begin = fun->lazyScript()->begin();
+        end = fun->lazyScript()->end();
     }
 
-    return hasArguments && hasApply;
+    return end - begin <= 100;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline unsigned
 TypeScript::NumTypeSets(JSScript *script)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -661,17 +661,20 @@ js::XDRScript(XDRState<mode> *xdr, Handl
             isBlock = obj->isBlock() ? 1 : 0;
         }
         if (!xdr->codeUint32(&isBlock))
             return false;
         if (isBlock == 0) {
             /* Code the nested function's enclosing scope. */
             uint32_t funEnclosingScopeIndex = 0;
             if (mode == XDR_ENCODE) {
-                RootedObject staticScope(cx, (*objp)->toFunction()->nonLazyScript()->enclosingStaticScope());
+                JSScript *innerScript = (*objp)->toFunction()->getOrCreateScript(cx);
+                if (!innerScript)
+                    return false;
+                RootedObject staticScope(cx, innerScript->enclosingStaticScope());
                 StaticScopeIter ssi(cx, staticScope);
                 if (ssi.done() || ssi.type() == StaticScopeIter::FUNCTION) {
                     JS_ASSERT(ssi.done() == !fun);
                     funEnclosingScopeIndex = UINT32_MAX;
                 } else {
                     funEnclosingScopeIndex = FindBlockIndex(script, ssi.block());
                     JS_ASSERT(funEnclosingScopeIndex < i);
                 }
@@ -1221,26 +1224,23 @@ SourceDataCache::purge()
 {
     js_delete(map_);
     map_ = NULL;
 }
 
 const jschar *
 ScriptSource::chars(JSContext *cx)
 {
-#ifdef USE_ZLIB
-    Rooted<JSStableString *> cached(cx, NULL);
-#endif
 #ifdef JS_THREADSAFE
     if (!ready())
         return cx->runtime()->sourceCompressorThread.currentChars();
 #endif
 #ifdef USE_ZLIB
     if (compressed()) {
-        cached = cx->runtime()->sourceDataCache.lookup(this);
+        JSStableString *cached = cx->runtime()->sourceDataCache.lookup(this);
         if (!cached) {
             const size_t nbytes = sizeof(jschar) * (length_ + 1);
             jschar *decompressed = static_cast<jschar *>(cx->malloc_(nbytes));
             if (!decompressed)
                 return NULL;
             if (!DecompressString(data.compressed, compressedLength_,
                                   reinterpret_cast<unsigned char *>(decompressed), nbytes)) {
                 JS_ReportOutOfMemory(cx);
@@ -1252,20 +1252,18 @@ ScriptSource::chars(JSContext *cx)
             if (!cached) {
                 js_free(decompressed);
                 return NULL;
             }
             cx->runtime()->sourceDataCache.put(this, cached);
         }
         return cached->chars().get();
     }
+#endif
     return data.source;
-#else
-    return data.source;
-#endif
 }
 
 JSStableString *
 ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
 {
     JS_ASSERT(start <= stop);
     const jschar *chars = this->chars(cx);
     if (!chars)
@@ -2319,18 +2317,21 @@ js::CloneScript(JSContext *cx, HandleObj
 
                 clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
             } else if (obj->isFunction()) {
                 RootedFunction innerFun(cx, obj->toFunction());
                 if (innerFun->isNative()) {
                     assertSameCompartment(cx, innerFun);
                     clone = innerFun;
                 } else {
-                    if (!innerFun->getOrCreateScript(cx))
-                        return NULL;
+                    if (innerFun->isInterpretedLazy()) {
+                        AutoCompartment ac(cx, innerFun);
+                        if (!innerFun->getOrCreateScript(cx))
+                            return NULL;
+                    }
                     RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
                     StaticScopeIter ssi(cx, staticScope);
                     RootedObject enclosingScope(cx);
                     if (!ssi.done() && ssi.type() == StaticScopeIter::BLOCK)
                         enclosingScope = objects[FindBlockIndex(src, ssi.block())];
                     else
                         enclosingScope = fun;
 
@@ -2766,36 +2767,42 @@ JSScript::markChildren(JSTracer *trc)
 #ifdef JS_ION
     ion::TraceIonScripts(trc, this);
 #endif
 }
 
 void
 LazyScript::markChildren(JSTracer *trc)
 {
-    if (parent_)
-        MarkScriptUnbarriered(trc, &parent_, "lazyScriptParent");
+    if (sourceObject_)
+        MarkObject(trc, &sourceObject_, "sourceObject");
+
+    if (parentFunction_)
+        MarkObject(trc, &parentFunction_, "parentFunction");
 
     if (script_)
-        MarkScriptUnbarriered(trc, &script_, "lazyScript");
+        MarkScript(trc, &script_, "realScript");
 
     HeapPtrAtom *freeVariables = this->freeVariables();
     for (size_t i = 0; i < numFreeVariables(); i++)
         MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable");
 
     HeapPtrFunction *innerFunctions = this->innerFunctions();
     for (size_t i = 0; i < numInnerFunctions(); i++)
         MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction");
 }
 
 void
 LazyScript::finalize(FreeOp *fop)
 {
     if (table_)
         fop->free_(table_);
+
+    if (originPrincipals_)
+        JS_DropPrincipals(fop->runtime(), originPrincipals_);
 }
 
 void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
     needsArgsAnalysis_ = true;
 }
@@ -2934,18 +2941,68 @@ JSScript::formalIsAliased(unsigned argSl
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
+LazyScript::LazyScript(void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions,
+                       JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
+  : script_(NULL),
+    parentFunction_(NULL),
+    sourceObject_(NULL),
+    table_(table),
+    originPrincipals_(NULL),
+    version_(version),
+    numFreeVariables_(numFreeVariables),
+    numInnerFunctions_(numInnerFunctions),
+    strict_(false),
+    bindingsAccessedDynamically_(false),
+    hasDebuggerStatement_(false),
+    directlyInsideEval_(false),
+    usesArgumentsAndApply_(false),
+    hasBeenCloned_(false),
+    begin_(begin),
+    end_(end),
+    lineno_(lineno),
+    column_(column)
+{
+    JS_ASSERT(this->version() == version);
+    JS_ASSERT(begin <= end);
+}
+
+void
+LazyScript::initScript(JSScript *script)
+{
+    JS_ASSERT(script && !script_);
+    script_ = script;
+}
+
+void
+LazyScript::setParent(JSFunction *parentFunction, ScriptSourceObject *sourceObject,
+                      JSPrincipals *originPrincipals)
+{
+    JS_ASSERT(sourceObject && !sourceObject_ && !parentFunction_ && !originPrincipals_);
+    parentFunction_ = parentFunction;
+    sourceObject_ = sourceObject;
+    originPrincipals_ = originPrincipals;
+    if (originPrincipals)
+        JS_HoldPrincipals(originPrincipals);
+}
+
+ScriptSourceObject *
+LazyScript::sourceObject() const
+{
+    return sourceObject_ ? &sourceObject_->asScriptSource() : NULL;
+}
+
 /* static */ LazyScript *
-LazyScript::Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
+LazyScript::Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
                    uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
 {
     JS_ASSERT(begin <= end);
 
     size_t bytes = (numFreeVariables * sizeof(HeapPtrAtom))
                  + (numInnerFunctions * sizeof(HeapPtrFunction));
 
     void *table = NULL;
@@ -2954,17 +3011,23 @@ LazyScript::Create(JSContext *cx, uint32
         if (!table)
             return NULL;
     }
 
     LazyScript *res = js_NewGCLazyScript(cx);
     if (!res)
         return NULL;
 
-    return new (res) LazyScript(table, numFreeVariables, numInnerFunctions, begin, end, lineno, column);
+    return new (res) LazyScript(table, numFreeVariables, numInnerFunctions, version,
+                                begin, end, lineno, column);
+}
+
+uint32_t LazyScript::staticLevel() const
+{
+    return parentFunction() ? parentFunction()->nonLazyScript()->staticLevel + 1 : 1;
 }
 
 void
 JSScript::updateBaselineOrIonRaw()
 {
 #ifdef JS_ION
     if (hasIonScript()) {
         baselineOrIonRaw = ion->method()->raw();
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -533,19 +533,22 @@ class JSScript : public js::gc::Cell
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
     bool            treatAsRunOnce:1; /* script is a lambda to treat as running once. */
     bool            hasRunOnce:1;     /* if treatAsRunOnce, whether script has executed. */
     bool            hasBeenCloned:1;  /* script has been reused for a clone. */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
 
-    /* Set for functions defined at the top level within an 'eval' script. */
+    // Set for functions defined at the top level within an 'eval' script.
     bool directlyInsideEval:1;
 
+    // Both 'arguments' and f.apply() are used. This is likely to be a wrapper.
+    bool usesArgumentsAndApply:1;
+
     /* script is attempted to be cloned anew at each callsite. This is
        temporarily needed for ParallelArray selfhosted code until type
        information can be made context sensitive. See discussion in
        bug 826148. */
     bool            shouldCloneAtCallsite:1;
 
     bool            isCallsiteClone:1; /* is a callsite clone; has a link to the original function */
 #ifdef JS_ION
@@ -1094,92 +1097,86 @@ class AliasedFormalIter
     const Binding &operator*() const { JS_ASSERT(!done()); return *p_; }
     const Binding *operator->() const { JS_ASSERT(!done()); return p_; }
     unsigned frameIndex() const { JS_ASSERT(!done()); return p_ - begin_; }
     unsigned scopeSlot() const { JS_ASSERT(!done()); return slot_; }
 };
 
 struct SourceCompressionToken;
 
-
 // Information about a script which may be (or has been) lazily compiled to
 // bytecode from its source.
 class LazyScript : public js::gc::Cell
 {
-    // Immediate parent in which the script is nested, or NULL if the parent
-    // has not been compiled yet. Lazy scripts are always functions within a
-    // global or eval script so there will be a parent.
-    JSScript *parent_;
-
     // If non-NULL, the script has been compiled and this is a forwarding
     // pointer to the result.
-    JSScript *script_;
+    HeapPtrScript script_;
+
+    // Immediate parent in which the script is nested, or NULL.
+    HeapPtrFunction parentFunction_;
+
+    // Source code object, or NULL if the script in which this is nested has
+    // not been compiled yet.
+    HeapPtrObject sourceObject_;
 
     // Heap allocated table with any free variables or inner functions.
     void *table_;
 
 #if JS_BITS_PER_WORD == 32
     uint32_t padding;
 #endif
 
-    uint32_t numFreeVariables_;
+    // Assorted bits that should really be in ScriptSourceObject.
+    JSPrincipals *originPrincipals_;
+    uint32_t version_ : 8;
+
+    uint32_t numFreeVariables_ : 24;
     uint32_t numInnerFunctions_ : 26;
 
-    bool strict_ : 1;
-    bool bindingsAccessedDynamically_ : 1;
-    bool hasDebuggerStatement_ : 1;
-    bool directlyInsideEval_:1;
-    bool hasBeenCloned_:1;
+    // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
+    uint32_t strict_ : 1;
+    uint32_t bindingsAccessedDynamically_ : 1;
+    uint32_t hasDebuggerStatement_ : 1;
+    uint32_t directlyInsideEval_:1;
+    uint32_t usesArgumentsAndApply_:1;
+    uint32_t hasBeenCloned_:1;
 
     // Source location for the script.
     uint32_t begin_;
     uint32_t end_;
     uint32_t lineno_;
     uint32_t column_;
 
-    LazyScript(void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions,
-               uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
-      : parent_(NULL),
-        script_(NULL),
-        table_(table),
-        numFreeVariables_(numFreeVariables),
-        numInnerFunctions_(numInnerFunctions),
-        strict_(false),
-        bindingsAccessedDynamically_(false),
-        hasDebuggerStatement_(false),
-        directlyInsideEval_(false),
-        hasBeenCloned_(false),
-        begin_(begin),
-        end_(end),
-        lineno_(lineno),
-        column_(column)
-    {
-        JS_ASSERT(begin <= end);
-    }
+    LazyScript(void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
+               uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
 
   public:
     static LazyScript *Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
-                              uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
+                              JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
 
-    void initParent(JSScript *parent) {
-        JS_ASSERT(parent && !parent_);
-        parent_ = parent;
-    }
-    JSScript *parent() const {
-        return parent_;
-    }
-
-    void initScript(JSScript *script) {
-        JS_ASSERT(script && !script_);
-        script_ = script;
-    }
+    void initScript(JSScript *script);
     JSScript *maybeScript() {
         return script_;
     }
 
+    JSFunction *parentFunction() const {
+        return parentFunction_;
+    }
+    ScriptSourceObject *sourceObject() const;
+    JSPrincipals *originPrincipals() const {
+        return originPrincipals_;
+    }
+    JSVersion version() const {
+        JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
+        return (version_ == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(version_);
+    }
+
+    void setParent(JSFunction *parentFunction, ScriptSourceObject *sourceObject,
+                   JSPrincipals *originPrincipals);
+
     uint32_t numFreeVariables() const {
         return numFreeVariables_;
     }
     HeapPtrAtom *freeVariables() {
         return (HeapPtrAtom *)table_;
     }
 
     uint32_t numInnerFunctions() const {
@@ -1212,39 +1209,48 @@ class LazyScript : public js::gc::Cell
 
     bool directlyInsideEval() const {
         return directlyInsideEval_;
     }
     void setDirectlyInsideEval() {
         directlyInsideEval_ = true;
     }
 
+    bool usesArgumentsAndApply() const {
+        return usesArgumentsAndApply_;
+    }
+    void setUsesArgumentsAndApply() {
+        usesArgumentsAndApply_ = true;
+    }
+
     bool hasBeenCloned() const {
         return hasBeenCloned_;
     }
     void setHasBeenCloned() {
         hasBeenCloned_ = true;
     }
 
     ScriptSource *source() const {
-        return parent()->scriptSource();
+        return sourceObject()->source();
     }
     uint32_t begin() const {
         return begin_;
     }
     uint32_t end() const {
         return end_;
     }
     uint32_t lineno() const {
         return lineno_;
     }
     uint32_t column() const {
         return column_;
     }
 
+    uint32_t staticLevel() const;
+
     Zone *zone() const {
         return Cell::tenuredZone();
     }
 
     void markChildren(JSTracer *trc);
     void finalize(js::FreeOp *fop);
 
     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -224,17 +224,16 @@ class Debugger::FrameRange
             entry = dbg->frames.lookup(frame);
             if (entry)
                 break;
             nextDebugger++;
         }
     }
 };
 
-
 /*** Breakpoints *********************************************************************************/
 
 BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
   : script(script), pc(pc), enabledCount(0),
     trapHandler(NULL), trapClosure(UndefinedValue())
 {
     JS_ASSERT(!script->hasBreakpointsAt(pc));
     JS_INIT_CLIST(&breakpoints);
@@ -681,17 +680,16 @@ Debugger::wrapEnvironment(JSContext *cx,
 }
 
 bool
 Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp)
 {
     assertSameCompartment(cx, object.get());
 
     if (vp.isObject()) {
-        // Do we need this RootedObject?
         RootedObject obj(cx, &vp.toObject());
 
         ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
         if (p) {
             vp.setObject(*p->value);
         } else {
             /* Create a new Debugger.Object for obj. */
             JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject();
@@ -2882,26 +2880,16 @@ DebuggerScript_getSourceMapUrl(JSContext
         args.rval().setString(str);
     } else {
         args.rval().setNull();
     }
 
     return true;
 }
 
-static bool
-EnsureFunctionHasScript(JSContext *cx, JSFunction *fun)
-{
-    if (fun->isInterpretedLazy()) {
-        AutoCompartment ac(cx, fun);
-        return fun->getOrCreateScript(cx);
-    }
-    return true;
-}
-
 static JSBool
 DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script);
     Debugger *dbg = Debugger::fromChildJSObject(obj);
 
     RootedObject result(cx, NewDenseEmptyArray(cx));
     if (!result)
@@ -2916,20 +2904,16 @@ DebuggerScript_getChildScripts(JSContext
         ObjectArray *objects = script->objects();
         RootedFunction fun(cx);
         RootedScript funScript(cx);
         RootedObject obj(cx), s(cx);
         for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) {
             obj = objects->vector[i];
             if (obj->isFunction()) {
                 fun = static_cast<JSFunction *>(obj.get());
-
-                if (!EnsureFunctionHasScript(cx, fun))
-                    return false;
-
                 funScript = fun->nonLazyScript();
                 s = dbg->wrapScript(cx, funScript);
                 if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
                     return false;
             }
         }
     }
     args.rval().setObject(*result);
@@ -4468,19 +4452,16 @@ DebuggerObject_getParameterNames(JSConte
 
     RootedFunction fun(cx, obj->toFunction());
     JSObject *result = NewDenseAllocatedArray(cx, fun->nargs);
     if (!result)
         return false;
     result->ensureDenseInitializedLength(cx, 0, fun->nargs);
 
     if (fun->isInterpreted()) {
-        if (!EnsureFunctionHasScript(cx, fun))
-            return false;
-
         JS_ASSERT(fun->nargs == fun->nonLazyScript()->bindings.numArgs());
 
         if (fun->nargs > 0) {
             BindingVector bindings(cx);
             RootedScript script(cx, fun->nonLazyScript());
             if (!FillBindingVector(script, &bindings))
                 return false;
             for (size_t i = 0; i < fun->nargs; i++) {
@@ -4512,19 +4493,16 @@ DebuggerObject_getScript(JSContext *cx, 
     }
 
     RootedFunction fun(cx, obj->toFunction());
     if (fun->isBuiltin()) {
         args.rval().setUndefined();
         return true;
     }
 
-    if (!EnsureFunctionHasScript(cx, fun))
-        return false;
-
     RootedScript script(cx, fun->nonLazyScript());
     RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
     if (!scriptObject)
         return false;
 
     args.rval().setObject(*scriptObject);
     return true;
 }