Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 30 May 2013 06:29:56 -0600
changeset 133448 d71234d65e90
parent 133447 87bbe2a5b08a
child 133449 8a990626f354
push id28767
push userbhackett@mozilla.com
push date2013-05-30 12:30 +0000
Treeherderresults
reviewersluke
bugs678037
milestone24.0a1
Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.
js/public/MemoryMetrics.h
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser-inl.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/frontend/SyntaxParseHandler.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/gc/Heap.h
js/src/gc/Marking.cpp
js/src/gc/Marking.h
js/src/ion/CodeGenerator.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/arm/Trampoline-arm.cpp
js/src/ion/x64/Trampoline-x64.cpp
js/src/ion/x86/Trampoline-x86.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsmemorymetrics.cpp
js/src/jsopcode.h
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/ForkJoin.cpp
js/src/vm/SelfHosting.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/nsXPConnect.cpp
mfbt/Util.h
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -147,53 +147,59 @@ struct RuntimeSizes
 struct ZoneStats
 {
     ZoneStats()
       : extra(NULL),
         gcHeapArenaAdmin(0),
         gcHeapUnusedGcThings(0),
         gcHeapStringsNormal(0),
         gcHeapStringsShort(0),
+        gcHeapLazyScripts(0),
         gcHeapTypeObjects(0),
         gcHeapIonCodes(0),
         stringCharsNonHuge(0),
+        lazyScripts(0),
         typeObjects(0),
         typePool(0),
         hugeStrings()
     {}
 
     ZoneStats(const ZoneStats &other)
       : extra(other.extra),
         gcHeapArenaAdmin(other.gcHeapArenaAdmin),
         gcHeapUnusedGcThings(other.gcHeapUnusedGcThings),
         gcHeapStringsNormal(other.gcHeapStringsNormal),
         gcHeapStringsShort(other.gcHeapStringsShort),
+        gcHeapLazyScripts(other.gcHeapLazyScripts),
         gcHeapTypeObjects(other.gcHeapTypeObjects),
         gcHeapIonCodes(other.gcHeapIonCodes),
         stringCharsNonHuge(other.stringCharsNonHuge),
+        lazyScripts(other.lazyScripts),
         typeObjects(other.typeObjects),
         typePool(other.typePool),
         hugeStrings()
     {
         hugeStrings.append(other.hugeStrings);
     }
 
     // Add other's numbers to this object's numbers.
     void add(ZoneStats &other) {
         #define ADD(x)  this->x += other.x
 
         ADD(gcHeapArenaAdmin);
         ADD(gcHeapUnusedGcThings);
 
         ADD(gcHeapStringsNormal);
         ADD(gcHeapStringsShort);
+        ADD(gcHeapLazyScripts);
         ADD(gcHeapTypeObjects);
         ADD(gcHeapIonCodes);
 
         ADD(stringCharsNonHuge);
+        ADD(lazyScripts);
         ADD(typeObjects);
         ADD(typePool);
 
         #undef ADD
 
         hugeStrings.append(other.hugeStrings);
     }
 
@@ -201,20 +207,22 @@ struct ZoneStats
     void   *extra;
 
     size_t gcHeapArenaAdmin;
     size_t gcHeapUnusedGcThings;
 
     size_t gcHeapStringsNormal;
     size_t gcHeapStringsShort;
 
+    size_t gcHeapLazyScripts;
     size_t gcHeapTypeObjects;
     size_t gcHeapIonCodes;
 
     size_t stringCharsNonHuge;
+    size_t lazyScripts;
     size_t typeObjects;
     size_t typePool;
 
     js::Vector<HugeStringInfo, 0, js::SystemAllocPolicy> hugeStrings;
 
     // The size of all the live things in the GC heap that don't belong to any
     // compartment.
     size_t GCHeapThingsSize();
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -186,16 +186,40 @@ TryEvalJSON(JSContext *cx, JSScript *cal
                 rval.set(tmp);
                 return EvalJSON_Success;
             }
         }
     }
     return EvalJSON_NotJSON;
 }
 
+static void
+MarkFunctionsWithinEvalScript(JSScript *script)
+{
+    // Mark top level functions in an eval script as being within an eval and,
+    // if applicable, inside a with statement.
+
+    if (!script->hasObjects())
+        return;
+
+    ObjectArray *objects = script->objects();
+    size_t start = script->innerObjectsStart();
+
+    for (size_t i = start; i < objects->length; i++) {
+        JSObject *obj = objects->vector[i];
+        if (obj->isFunction()) {
+            JSFunction *fun = obj->toFunction();
+            if (fun->hasScript())
+                fun->nonLazyScript()->directlyInsideEval = true;
+            else if (fun->isInterpretedLazy())
+                fun->lazyScript()->setDirectlyInsideEval();
+        }
+    }
+}
+
 // Define subset of ExecuteType so that casting performs the injection.
 enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
 
 // Common code implementing direct and indirect eval.
 //
 // Evaluate call.argv[2], if it is a string, in the context of the given calling
 // frame, with the provided scope chain, with the semantics of either a direct
 // or indirect eval (see ES5 10.4.2).  If this is an indirect eval, scopeobj
@@ -290,16 +314,18 @@ EvalKernel(JSContext *cx, const CallArgs
                .setNoScriptRval(false)
                .setPrincipals(principals)
                .setOriginPrincipals(originPrincipals);
         JSScript *compiled = frontend::CompileScript(cx, scopeobj, callerScript, options,
                                                      chars.get(), length, stableStr, staticLevel);
         if (!compiled)
             return false;
 
+        MarkFunctionsWithinEvalScript(compiled);
+
         esg.setNewScript(compiled);
     }
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
                          NullFramePtr() /* evalInFrame */, args.rval().address());
 }
 
 bool
@@ -352,16 +378,18 @@ js::DirectEvalFromIon(JSContext *cx,
                .setNoScriptRval(false)
                .setPrincipals(principals)
                .setOriginPrincipals(originPrincipals);
         JSScript *compiled = frontend::CompileScript(cx, scopeobj, callerScript, options,
                                                      chars.get(), length, stableStr, staticLevel);
         if (!compiled)
             return false;
 
+        MarkFunctionsWithinEvalScript(compiled);
+
         esg.setNewScript(compiled);
     }
 
     // Primitive 'this' values should have been filtered out by Ion. If boxed,
     // the calling frame cannot be updated to store the new object.
     JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull());
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL),
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -18,16 +18,17 @@
 
 #include "frontend/ParseMaps-inl.h"
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
 #include "frontend/SharedContext-inl.h"
 
 using namespace js;
 using namespace js::frontend;
+using mozilla::Maybe;
 
 static bool
 CheckLength(JSContext *cx, size_t length)
 {
     // Note this limit is simply so we can store sourceStart and sourceEnd in
     // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
     // is using size_t internally already.
     if (length > UINT32_MAX) {
@@ -79,16 +80,17 @@ frontend::CompileScript(JSContext *cx, H
                         HandleScript evalCaller,
                         const CompileOptions &options,
                         const jschar *chars, size_t length,
                         JSString *source_ /* = NULL */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionToken *extraSct /* = NULL */)
 {
     RootedString source(cx, source_);
+    SkipRoot skip(cx, &chars);
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(evalCaller, options.compileAndGo);
     JS_ASSERT_IF(evalCaller, options.forEval);
     JS_ASSERT_IF(staticLevel != 0, evalCaller);
@@ -114,25 +116,38 @@ frontend::CompileScript(JSContext *cx, H
         break;
       case CompileOptions::LAZY_SOURCE:
         ss->setSourceRetrievable();
         break;
       case CompileOptions::NO_SOURCE:
         break;
     }
 
-    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
+    Maybe<Parser<SyntaxParseHandler> > syntaxParser;
+    if (options.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);
     if (!parser.init())
         return NULL;
     parser.sct = sct;
 
     GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
 
-    ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0);
-    if (!pc.init())
+    // 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));
@@ -145,18 +160,20 @@ frontend::CompileScript(JSContext *cx, H
     if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL))
         return NULL;
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL;
     JS_ASSERT_IF(globalScope, globalScope->isNative());
     JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
 
+    BytecodeEmitter::EmitterMode emitterMode =
+        options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller,
-                        !!globalScope, options.lineno, options.selfHostingMode);
+                        !!globalScope, options.lineno, emitterMode);
     if (!bce.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) {
@@ -173,36 +190,59 @@ frontend::CompileScript(JSContext *cx, H
 
         if (evalCaller && evalCaller->functionOrCallerFunction()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
              * function captured in case it refers to an upvar, and someone
              * wishes to decompile it while it's running.
              */
             JSFunction *fun = evalCaller->functionOrCallerFunction();
-            ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
+            ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), fun->strict());
             if (!funbox)
                 return NULL;
             bce.objectList.add(funbox);
         }
     }
 
     bool canHaveDirectives = true;
     for (;;) {
         TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF) {
             if (tt == TOK_EOF)
                 break;
             JS_ASSERT(tt == TOK_ERROR);
             return NULL;
         }
 
+        TokenStream::Position pos(parser.keepAtoms);
+        parser.tokenStream.tell(&pos);
+
         ParseNode *pn = parser.statement();
-        if (!pn)
-            return NULL;
+        if (!pn) {
+            if (parser.hadAbortedSyntaxParse()) {
+                // Parsing inner functions lazily may lead the parser into an
+                // unrecoverable state and may require starting over on the top
+                // level statement. Restart the parse; syntax parsing has
+                // already been disabled for the parser and the result will not
+                // be ambiguous.
+                parser.clearAbortedSyntaxParse();
+                parser.tokenStream.seek(pos);
+                pc.destroy();
+                pc.construct(&parser, (GenericParseContext *) NULL, &globalsc,
+                             staticLevel, /* bodyid = */ 0);
+                if (!pc.ref().init())
+                    return NULL;
+                JS_ASSERT(parser.pc == pc.addr());
+                pn = parser.statement();
+            }
+            if (!pn) {
+                JS_ASSERT(!parser.hadAbortedSyntaxParse());
+                return NULL;
+            }
+        }
 
         if (canHaveDirectives) {
             if (!parser.maybeParseDirective(pn, &canHaveDirectives))
                 return NULL;
         }
 
         if (!FoldConstants(cx, &pn, &parser))
             return NULL;
@@ -218,33 +258,33 @@ frontend::CompileScript(JSContext *cx, H
     if (!SetSourceMap(cx, parser.tokenStream, ss, script))
         return NULL;
 
     if (evalCaller && evalCaller->functionOrCallerFunction()) {
         // Watch for uses of 'arguments' within the evaluated script, both as
         // free variables and as variables redeclared with 'var'.
         RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
         HandlePropertyName arguments = cx->names().arguments;
-        for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
+        for (AtomDefnRange r = pc.ref().lexdeps->all(); !r.empty(); r.popFront()) {
             if (r.front().key() == arguments) {
                 if (!CheckArgumentsWithinEval(cx, parser, fun))
                     return NULL;
             }
         }
-        for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
+        for (AtomDefnListMap::Range r = pc.ref().decls().all(); !r.empty(); r.popFront()) {
             if (r.front().key() == arguments) {
                 if (!CheckArgumentsWithinEval(cx, parser, fun))
                     return NULL;
             }
         }
 
         // If the eval'ed script contains any debugger statement, force construction
         // of arguments objects for the caller script and any other scripts it is
         // transitively nested inside.
-        if (pc.sc->hasDebuggerStatement()) {
+        if (pc.ref().sc->hasDebuggerStatement()) {
             RootedObject scope(cx, scopeChain);
             while (scope->isScope() || scope->isDebugScope()) {
                 if (scope->isCall() && !scope->asCall().isForEval()) {
                     RootedScript script(cx, scope->asCall().callee().nonLazyScript());
                     if (script->argumentsHasVarBinding()) {
                         if (!JSScript::argumentsOptimizationFailed(cx, script))
                             return NULL;
                     }
@@ -268,62 +308,76 @@ frontend::CompileScript(JSContext *cx, H
 
     if (sct == &mysct && !sct->complete())
         return NULL;
 
     return script;
 }
 
 bool
-frontend::ParseScript(JSContext *cx, HandleObject scopeChain,
-                      const CompileOptions &options, StableCharPtr chars, size_t length)
+frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
+                              const jschar *chars, size_t length)
 {
-    if (!CheckLength(cx, length))
+    CompileOptions options(cx);
+    options.setPrincipals(cx->compartment->principals)
+           .setOriginPrincipals(lazy->parent()->originPrincipals)
+           .setVersion(lazy->parent()->getVersion())
+           .setFileAndLine(lazy->parent()->filename(), lazy->lineno())
+           .setColumn(lazy->column())
+           .setCompileAndGo(lazy->parent()->compileAndGo)
+           .setNoScriptRval(false)
+           .setSelfHostingMode(false);
+
+    Parser<FullParseHandler> parser(cx, options, chars, length,
+                                    /* foldConstants = */ true, NULL, lazy);
+    if (!parser.init())
         return false;
 
-    Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
-    if (!parser.init()) {
-        cx->clearPendingException();
+    RootedObject enclosingScope(cx, lazy->parent()->function());
+
+    ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->parent()->staticLevel + 1,
+                                                  lazy->strict());
+    if (!pn)
         return false;
-    }
 
-    GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
+    JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, lazy->source()));
+    if (!sourceObject)
+        return false;
 
-    ParseContext<SyntaxParseHandler> pc(&parser, NULL, &globalsc, 0, /* bodyid = */ 0);
-    if (!pc.init()) {
-        cx->clearPendingException();
+    Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
+                                                  options, lazy->parent()->staticLevel + 1,
+                                                  sourceObject, lazy->begin(), lazy->end()));
+    if (!script)
         return false;
-    }
 
-    for (;;) {
-        TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
-        if (tt <= TOK_EOF) {
-            if (tt == TOK_EOF)
-                break;
-            JS_ASSERT(tt == TOK_ERROR);
-            cx->clearPendingException();
-            return false;
-        }
+    script->bindings = pn->pn_funbox->bindings;
 
-        if (!parser.statement()) {
-            cx->clearPendingException();
-            return false;
-        }
-    }
+    if (lazy->directlyInsideEval())
+        script->directlyInsideEval = true;
 
-    return true;
+    bool hasGlobalScope = lazy->parent()->compileAndGo;
+
+    BytecodeEmitter bce(/* parent = */ NULL, &parser, pn->pn_funbox, script, options.forEval,
+                        /* evalCaller = */ NullPtr(), hasGlobalScope,
+                        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
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 bool
 frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
                               const AutoNameVector &formals, const jschar *chars, size_t length,
                               bool isAsmJSRecompile)
 {
+    SkipRoot skip(cx, &chars);
+
     if (!CheckLength(cx, length))
         return false;
     ScriptSource *ss = cx->new_<ScriptSource>();
     if (!ss)
         return false;
     if (options.filename && !ss->setFilename(cx, options.filename))
         return false;
     JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss));
@@ -331,19 +385,27 @@ 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;
     }
 
+    Maybe<Parser<SyntaxParseHandler> > syntaxParser;
+    if (options.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);
+    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
+                                    options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     if (!parser.init())
         return false;
     parser.sct = &sct;
 
     JS_ASSERT(fun);
 
     fun->setArgCount(formals.length());
 
@@ -369,31 +431,39 @@ frontend::CompileFunctionBody(JSContext 
     if (!script)
         return false;
 
     // If the context is strict, immediately parse the body in strict
     // mode. Otherwise, we parse it normally. If we see a "use strict"
     // directive, we backup and reparse it as strict.
     TokenStream::Position start(parser.keepAtoms);
     parser.tokenStream.tell(&start);
-    bool initiallyStrict = StrictModeFromContext(cx);
+    bool strict = StrictModeFromContext(cx);
     bool becameStrict;
     FunctionBox *funbox;
-    ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
-                                                  initiallyStrict, &becameStrict);
-    if (!pn) {
-        if (initiallyStrict || !becameStrict || parser.tokenStream.hadError())
-            return false;
+    ParseNode *pn;
+    while (true) {
+        pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
+                                           strict, &becameStrict);
+        if (pn)
+            break;
 
-        // Reparse in strict mode.
+        if (parser.hadAbortedSyntaxParse()) {
+            // Hit some unrecoverable ambiguity during an inner syntax parse.
+            // Syntax parsing has now been disabled in the parser, so retry
+            // the parse.
+            parser.clearAbortedSyntaxParse();
+        } else {
+            // If the function became strict, reparse in strict mode.
+            if (strict || !becameStrict || parser.tokenStream.hadError())
+                return false;
+            strict = true;
+        }
+
         parser.tokenStream.seek(start);
-        pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
-                                           /* strict = */ true);
-        if (!pn)
-            return false;
     }
 
     if (!NameFunctions(cx, pn))
         return false;
 
     if (fn->pn_body) {
         JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
         fn->pn_body->append(pn);
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -14,18 +14,18 @@ namespace frontend {
 
 JSScript *
 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);
 
 bool
-ParseScript(JSContext *cx, HandleObject scopeChain,
-            const CompileOptions &options, StableCharPtr chars, size_t length);
+CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
+                    const jschar *chars, size_t length);
 
 bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
                     const AutoNameVector &formals, const jschar *chars, size_t length,
                     bool isAsmJSRecompile = false);
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -87,17 +87,17 @@ struct frontend::StmtInfoBCE : public St
         JS_ASSERT(type == STMT_TRY || type == STMT_FINALLY);
         return continues;
     }
 };
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
                                  Parser<FullParseHandler> *parser, SharedContext *sc,
                                  HandleScript script, bool insideEval, HandleScript evalCaller,
-                                 bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode)
+                                 bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     parent(parent),
     script(sc->context, script),
     prolog(sc->context, lineNum),
     main(sc->context, lineNum),
     current(&main),
     parser(parser),
     evalCaller(evalCaller),
@@ -112,17 +112,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     emitLevel(0),
     constList(sc->context),
     typesetCount(0),
     hasSingletons(false),
     emittingForInit(false),
     emittingRunOnceLambda(false),
     insideEval(insideEval),
     hasGlobalScope(hasGlobalScope),
-    selfHostingMode(selfHostingMode)
+    emitterMode(emitterMode)
 {
     JS_ASSERT_IF(evalCaller, insideEval);
 }
 
 bool
 BytecodeEmitter::init()
 {
     return atomIndices.ensureMap(sc->context);
@@ -823,33 +823,34 @@ ClonedBlockDepth(BytecodeEmitter *bce)
     for (StaticBlockObject *b = bce->blockChain; b; b = b->enclosingBlock()) {
         if (b->needsClone())
             ++clonedBlockDepth;
     }
 
     return clonedBlockDepth;
 }
 
-static uint16_t
-AliasedNameToSlot(HandleScript script, PropertyName *name)
+static bool
+LookupAliasedName(HandleScript script, PropertyName *name, uint16_t *pslot)
 {
     /*
      * Beware: BindingIter may contain more than one Binding for a given name
      * (in the case of |function f(x,x) {}|) but only one will be aliased.
      */
     unsigned slot = CallObject::RESERVED_SLOTS;
-    for (BindingIter bi(script); ; bi++) {
+    for (BindingIter bi(script); !bi.done(); bi++) {
         if (bi->aliased()) {
-            if (bi->name() == name)
-                return slot;
+            if (bi->name() == name) {
+                *pslot = slot;
+                return true;
+            }
             slot++;
         }
     }
-
-    return 0;
+    return false;
 }
 
 static bool
 EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
 {
     unsigned skippedScopes = 0;
     BytecodeEmitter *bceOfDef = bce;
     if (pn->isUsed()) {
@@ -871,23 +872,23 @@ EmitAliasedVarOp(JSContext *cx, JSOp op,
     } else {
         JS_ASSERT(pn->isDefn());
         JS_ASSERT(pn->pn_cookie.level() == bce->script->staticLevel);
     }
 
     ScopeCoordinate sc;
     if (IsArgOp(pn->getOp())) {
         sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
-        sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
+        JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
     } else {
         JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
         unsigned local = pn->pn_cookie.slot();
         if (local < bceOfDef->script->bindings.numVars()) {
             sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
-            sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
+            JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
         } else {
             unsigned depth = local - bceOfDef->script->bindings.numVars();
             StaticBlockObject *b = bceOfDef->blockChain;
             while (!b->containsVarAtDepth(depth)) {
                 if (b->needsClone())
                     skippedScopes++;
                 b = b->enclosingBlock();
             }
@@ -898,18 +899,26 @@ EmitAliasedVarOp(JSContext *cx, JSOp op,
 
     return EmitAliasedVarOp(cx, op, sc, bce);
 }
 
 static bool
 EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
+    JS_ASSERT(!pn->pn_cookie.isFree());
+
+    if (IsAliasedVarOp(op)) {
+        ScopeCoordinate sc;
+        sc.hops = pn->pn_cookie.level();
+        sc.slot = pn->pn_cookie.slot();
+        return EmitAliasedVarOp(cx, op, sc, bce);
+    }
+
     JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
-    JS_ASSERT(!pn->pn_cookie.isFree());
 
     if (!bce->isAliasedName(pn)) {
         JS_ASSERT(pn->isUsed() || pn->isDefn());
         JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
         JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
         return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
     }
 
@@ -931,24 +940,34 @@ GetIncDecInfo(ParseNodeKind kind, bool *
     *post = kind == PNK_POSTINCREMENT || kind == PNK_POSTDECREMENT;
     return (kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT) ? JSOP_ADD : JSOP_SUB;
 }
 
 static bool
 EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
 {
     JSOp op = pn->pn_kid->getOp();
-    JS_ASSERT(IsLocalOp(op) || IsArgOp(op));
+    JS_ASSERT(IsArgOp(op) || IsLocalOp(op) || IsAliasedVarOp(op));
     JS_ASSERT(pn->pn_kid->isKind(PNK_NAME));
     JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
 
     bool post;
     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
-    JSOp getOp = IsLocalOp(op) ? JSOP_GETLOCAL : JSOP_GETARG;
-    JSOp setOp = IsLocalOp(op) ? JSOP_SETLOCAL : JSOP_SETARG;
+
+    JSOp getOp, setOp;
+    if (IsLocalOp(op)) {
+        getOp = JSOP_GETLOCAL;
+        setOp = JSOP_SETLOCAL;
+    } else if (IsArgOp(op)) {
+        getOp = JSOP_GETARG;
+        setOp = JSOP_SETARG;
+    } else {
+        getOp = JSOP_GETALIASEDVAR;
+        setOp = JSOP_SETALIASEDVAR;
+    }
 
     if (!EmitVarOp(cx, pn->pn_kid, getOp, bce))              // V
         return false;
     if (Emit1(cx, bce, JSOP_POS) < 0)                        // N
         return false;
     if (post && Emit1(cx, bce, JSOP_DUP) < 0)                // N? N
         return false;
     if (Emit1(cx, bce, JSOP_ONE) < 0)                        // N? N 1
@@ -1072,70 +1091,132 @@ EmitEnterBlock(JSContext *cx, BytecodeEm
 
         blockObj->setAliased(i, bce->isAliasedName(dn));
     }
 
     return true;
 }
 
 /*
- * Try to convert a *NAME op to a *GNAME op, which optimizes access to
- * globals. Return true if a conversion was made.
- *
- * Don't convert to *GNAME ops within strict-mode eval, since access
- * to a "global" might merely be to a binding local to that eval:
- *
- *   "use strict";
- *   var x = "global";
- *   eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
- *
- * Outside eval code, access to an undeclared global is a strict mode error:
- *
- *   "use strict";
- *   function foo()
- *   {
- *     undeclared = 17; // throws ReferenceError
- *   }
- *   foo();
- *
- * In self-hosting mode, JSOP_*NAME is unconditionally converted to
- * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
- * intrinsics holder in the global object, into which any missing values are
- * cloned lazily upon first access.
+ * Try to convert a *NAME op with a free name to a more specialized GNAME,
+ * INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
+ * Return true if a conversion was made.
  */
 static bool
-TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
+TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
 {
-    if (bce->selfHostingMode) {
-        switch (*op) {
-          case JSOP_NAME:     *op = JSOP_GETINTRINSIC; break;
-          case JSOP_SETNAME:  *op = JSOP_SETINTRINSIC; break;
+    /*
+     * In self-hosting mode, JSOP_*NAME is unconditionally converted to
+     * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
+     * intrinsics holder in the global object, into which any missing values are
+     * cloned lazily upon first access.
+     */
+    if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
+        JSOp op;
+        switch (pn->getOp()) {
+          case JSOP_NAME:     op = JSOP_GETINTRINSIC; break;
+          case JSOP_SETNAME:  op = JSOP_SETINTRINSIC; break;
           /* Other *NAME ops aren't (yet) supported in self-hosted code. */
           default: JS_NOT_REACHED("intrinsic");
         }
+        pn->setOp(op);
         return true;
     }
+
+    /*
+     * 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) {
+        size_t hops = 0;
+        FunctionBox *funbox = bce->sc->asFunctionBox();
+        if (funbox->hasExtensibleScope())
+            return false;
+        if (funbox->function()->atom() == pn->pn_atom)
+            return false;
+        if (funbox->function()->isHeavyweight()) {
+            hops++;
+            if (funbox->function()->isNamedLambda())
+                hops++;
+        }
+        if (bce->script->directlyInsideEval)
+            return false;
+        RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
+        for (StaticScopeIter ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
+            if (ssi.type() != StaticScopeIter::FUNCTION) {
+                if (ssi.hasDynamicScopeObject())
+                    hops++;
+                continue;
+            }
+            RootedScript script(bce->sc->context, ssi.funScript());
+            if (script->function()->atom() == pn->pn_atom)
+                return false;
+            if (ssi.hasDynamicScopeObject()) {
+                uint16_t slot;
+                if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
+                    JSOp op;
+                    switch (pn->getOp()) {
+                      case JSOP_NAME:     op = JSOP_GETALIASEDVAR; break;
+                      case JSOP_SETNAME:  op = JSOP_SETALIASEDVAR; break;
+                      default: return false;
+                    }
+                    pn->setOp(op);
+                    JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->sc->context, hops, slot));
+                    return true;
+                }
+                hops++;
+            }
+
+            if (script->funHasExtensibleScope || script->directlyInsideEval)
+                return false;
+        }
+    }
+
+    /*
+     * Try to convert free names in global scope to GNAME opcodes.
+     *
+     * This conversion is not made if we are in strict mode. In eval code nested
+     * within (strict mode) eval code, access to an undeclared "global" might
+     * merely be to a binding local to that outer eval:
+     *
+     *   "use strict";
+     *   var x = "global";
+     *   eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
+     *
+     * Outside eval code, access to an undeclared global is a strict mode error:
+     *
+     *   "use strict";
+     *   function foo()
+     *   {
+     *     undeclared = 17; // throws ReferenceError
+     *   }
+     *   foo();
+     */
     if (bce->script->compileAndGo &&
         bce->hasGlobalScope &&
         !(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->mightAliasLocals()) &&
         !pn->isDeoptimized() &&
         !(bce->sc->strict && bce->insideEval))
     {
         // If you change anything here, you might also need to change
         // js::ReportIfUndeclaredVarAssignment.
-        switch (*op) {
-          case JSOP_NAME:     *op = JSOP_GETGNAME; break;
-          case JSOP_SETNAME:  *op = JSOP_SETGNAME; break;
+        JSOp op;
+        switch (pn->getOp()) {
+          case JSOP_NAME:     op = JSOP_GETGNAME; break;
+          case JSOP_SETNAME:  op = JSOP_SETGNAME; break;
           case JSOP_SETCONST:
             /* Not supported. */
             return false;
           default: JS_NOT_REACHED("gname");
         }
+        pn->setOp(op);
         return true;
     }
+
     return false;
 }
 
 /*
  * BindNameToSlotHelper attempts to optimize name gets and sets to stack slot
  * loads and stores, given the compile-time information in bce and a PNK_NAME
  * node pn.  It returns false on error, true on success.
  *
@@ -1220,36 +1301,33 @@ BindNameToSlotHelper(JSContext *cx, Byte
              */
             if (bce->emittingForInit)
                 return true;
 
             /*
              * If this is an eval in the global scope, then unbound variables
              * must be globals, so try to use GNAME ops.
              */
-            if (!caller->functionOrCallerFunction() && TryConvertToGname(bce, pn, &op)) {
-                pn->setOp(op);
+            if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
                 pn->pn_dflags |= PND_BOUND;
                 return true;
             }
 
             /*
              * Out of tricks, so we must rely on PICs to optimize named
              * accesses from direct eval called from function code.
              */
             return true;
         }
 
         /* Optimize accesses to undeclared globals. */
-        if (!TryConvertToGname(bce, pn, &op))
+        if (!TryConvertFreeName(bce, pn))
             return true;
 
-        pn->setOp(op);
         pn->pn_dflags |= PND_BOUND;
-
         return true;
     }
 
     /*
      * At this point, we are only dealing with uses that have already been
      * bound to definitions via pn_lexdef. The rest of this routine converts
      * the parse node of the use from its initial JSOP_*NAME* op to a LOCAL/ARG
      * op. This requires setting the node's pn_cookie with a pair (level, slot)
@@ -1389,17 +1467,17 @@ BindNameToSlotHelper(JSContext *cx, Byte
  * and we do not want to allow self-hosted code to use the dynamic scope.
  */
 static bool
 BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!BindNameToSlotHelper(cx, bce, pn))
         return false;
 
-    if (bce->selfHostingMode && !pn->isBound()) {
+    if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
         bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
         return false;
     }
 
     return true;
 }
 
 /*
@@ -1716,16 +1794,19 @@ EmitNameOp(JSContext *cx, BytecodeEmitte
             op = JSOP_CALLGNAME;
             break;
           case JSOP_GETARG:
             op = JSOP_CALLARG;
             break;
           case JSOP_GETLOCAL:
             op = JSOP_CALLLOCAL;
             break;
+          case JSOP_GETALIASEDVAR:
+            op = JSOP_CALLALIASEDVAR;
+            break;
           default:
             JS_ASSERT(op == JSOP_CALLEE);
             break;
         }
     }
 
     if (op == JSOP_CALLEE) {
         if (Emit1(cx, bce, op) < 0)
@@ -2454,17 +2535,17 @@ frontend::EmitFunctionScript(JSContext *
         bce->switchToProlog();
         if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
             return false;
         InternalBindingsHandle bindings(bce->script, &bce->script->bindings);
         unsigned varIndex = Bindings::argumentsVarIndex(cx, bindings);
         if (bce->script->varIsAliased(varIndex)) {
             ScopeCoordinate sc;
             sc.hops = 0;
-            sc.slot = AliasedNameToSlot(bce->script, cx->names().arguments);
+            JS_ALWAYS_TRUE(LookupAliasedName(bce->script, cx->names().arguments, &sc.slot));
             if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
                 return false;
         } else {
             if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, varIndex, bce))
                 return false;
         }
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
@@ -2493,38 +2574,20 @@ frontend::EmitFunctionScript(JSContext *
 
     /*
      * If this function is only expected to run once, mark the script so that
      * initializers created within it may be given more precise types.
      */
     if (bce->parent && bce->parent->emittingRunOnceLambda)
         bce->script->treatAsRunOnce = true;
 
-    /*
-     * Mark as singletons any function which will only be executed once, or
-     * which is inner to a lambda we only expect to run once. In the latter
-     * case, if the lambda runs multiple times then CloneFunctionObject will
-     * make a deep clone of its contents.
-     */
-    bool singleton =
-        cx->typeInferenceEnabled() &&
-        bce->script->compileAndGo &&
-        bce->parent &&
-        (bce->parent->checkSingletonContext() ||
-         (!bce->parent->isInLoop() &&
-          bce->parent->parent &&
-          bce->parent->parent->emittingRunOnceLambda));
-
     /* Initialize fun->script() so that the debugger has a valid fun->script(). */
     RootedFunction fun(cx, bce->script->function());
     JS_ASSERT(fun->isInterpreted());
-    JS_ASSERT(!fun->hasScript());
     fun->setScript(bce->script);
-    if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
-        return false;
 
     bce->tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 static bool
 MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
@@ -3251,17 +3314,23 @@ EmitAssignment(JSContext *cx, BytecodeEm
                 JS_ASSERT(lhs->pn_cookie.isFree());
                 if (!EmitAtomOp(cx, lhs, JSOP_GETGNAME, bce))
                     return false;
             } else if (lhs->isOp(JSOP_SETINTRINSIC)) {
                 JS_ASSERT(lhs->pn_cookie.isFree());
                 if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
                     return false;
             } else {
-                JSOp op = lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL;
+                JSOp op;
+                switch (lhs->getOp()) {
+                  case JSOP_SETARG: op = JSOP_GETARG; break;
+                  case JSOP_SETLOCAL: op = JSOP_GETLOCAL; break;
+                  case JSOP_SETALIASEDVAR: op = JSOP_GETALIASEDVAR; break;
+                  default: JS_NOT_REACHED("Bad op");
+                }
                 if (!EmitVarOp(cx, lhs, op, bce))
                     return false;
             }
             break;
           case PNK_DOT: {
             if (Emit1(cx, bce, JSOP_DUP) < 0)
                 return false;
             bool isLength = (lhs->pn_atom == cx->names().length);
@@ -3315,17 +3384,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
       case PNK_NAME:
         if (lhs->isConst()) {
             if (!rhs) {
                 bce->reportError(lhs, JSMSG_BAD_FOR_LEFTSIDE);
                 return false;
             }
             break;
         }
-        if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL)) {
+        if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL) || lhs->isOp(JSOP_SETALIASEDVAR)) {
             if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
                 return false;
         } else {
             if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
                 return false;
         }
         break;
       case PNK_DOT:
@@ -4328,30 +4397,59 @@ EmitFor(JSContext *cx, BytecodeEmitter *
     return pn->pn_left->isKind(PNK_FORIN)
            ? EmitForIn(cx, bce, pn, top)
            : EmitNormalFor(cx, bce, pn, top);
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
-    RootedFunction fun(cx, pn->pn_funbox->function());
+    FunctionBox *funbox = pn->pn_funbox;
+    RootedFunction fun(cx, funbox->function());
     if (fun->isNative()) {
         JS_ASSERT(IsAsmJSModuleNative(fun->native()));
         return true;
     }
-    if (fun->hasScript()) {
+    JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
+
+    /*
+     * Set the EMITTEDFUNCTION flag in function definitions once they have been
+     * emitted. Function definitions that need hoisting to the top of the
+     * function will be seen by EmitFunc in two places.
+     */
+    if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
+        JS_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
         JS_ASSERT(pn->functionIsHoisted());
         JS_ASSERT(bce->sc->isFunctionBox());
         return true;
     }
 
-    {
+    pn->pn_dflags |= PND_EMITTEDFUNCTION;
+
+    /*
+     * Mark as singletons any function which will only be executed once, or
+     * which is inner to a lambda we only expect to run once. In the latter
+     * case, if the lambda runs multiple times then CloneFunctionObject will
+     * make a deep clone of its contents.
+     */
+    bool singleton =
+        cx->typeInferenceEnabled() &&
+        bce->script->compileAndGo &&
+        (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);
+    } else {
         SharedContext *outersc = bce->sc;
-        FunctionBox *funbox = pn->pn_funbox;
 
         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.
         Rooted<JSScript*> parent(cx, bce->script);
         CompileOptions options(cx);
@@ -4397,17 +4495,17 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
             if (!script)
                 return false;
 
             script->bindings = funbox->bindings;
 
             uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
             BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval,
                                  bce->evalCaller, bce->hasGlobalScope, lineNum,
-                                 bce->selfHostingMode);
+                                 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;
         }
     }
@@ -4836,17 +4934,18 @@ EmitCallOrNew(JSContext *cx, BytecodeEmi
                              callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
     bool emitArgs = true;
     ParseNode *pn2 = pn->pn_head;
     switch (pn2->getKind()) {
       case PNK_NAME:
-        if (bce->selfHostingMode && pn2->name() == cx->names().callFunction)
+        if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
+            pn2->name() == cx->names().callFunction)
         {
             /*
              * Special-casing of callFunction to emit bytecode that directly
              * invokes the callee with the correct |this| object and arguments.
              * callFunction(fun, thisArg, ...args) thus becomes:
              * - emit lookup for fun
              * - emit lookup for thisArg
              * - emit lookups for ...args
@@ -5060,16 +5159,17 @@ EmitIncOrDec(JSContext *cx, BytecodeEmit
         pn2->setOp(JSOP_SETNAME);
         if (!BindNameToSlot(cx, bce, pn2))
             return false;
         JSOp op = pn2->getOp();
         bool maySet;
         switch (op) {
           case JSOP_SETLOCAL:
           case JSOP_SETARG:
+          case JSOP_SETALIASEDVAR:
           case JSOP_SETNAME:
           case JSOP_SETGNAME:
             maySet = true;
             break;
           default:
             maySet = false;
         }
         if (op == JSOP_CALLEE) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -125,30 +125,44 @@ struct BytecodeEmitter
                                                 expected to run once. */
 
     bool            insideEval:1;       /* True if compiling an eval-expression or a function
                                            nested inside an eval. */
 
     const bool      hasGlobalScope:1;   /* frontend::CompileScript's scope chain is the
                                            global object */
 
-    const bool      selfHostingMode:1;  /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
-                                           and assert that JSOP_NAME and JSOP_*GNAME
-                                           don't ever get emitted. See the comment for
-                                           the field |selfHostingMode| in Parser.h for details. */
+    enum EmitterMode {
+        Normal,
+
+        /*
+         * Emit JSOP_CALLINTRINSIC instead of JSOP_NAME and assert that
+         * JSOP_NAME and JSOP_*GNAME don't ever get emitted. See the comment
+         * for the field |selfHostingMode| in Parser.h for details.
+         */
+        SelfHosting,
+
+        /*
+         * Check the static scope chain of the root function for resolving free
+         * variable accesses in the script.
+         */
+        LazyFunction
+    };
+
+    const EmitterMode emitterMode;
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
     BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
                     HandleScript script, bool insideEval, HandleScript evalCaller,
-                    bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode = false);
+                    bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
 
     bool isAliasedName(ParseNode *pn);
 
     JS_ALWAYS_INLINE
     bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
         AtomIndexAddPtr p = atomIndices->lookupForAdd(atom);
         if (p) {
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -267,19 +267,22 @@ FoldConstants<FullParseHandler>(JSContex
     switch (pn->getArity()) {
       case PN_CODE:
         if (pn->pn_funbox->useAsmOrInsideUseAsm() && cx->hasOption(JSOPTION_ASMJS))
             return true;
         if (pn->getKind() == PNK_MODULE) {
             if (!FoldConstants(cx, &pn->pn_body, parser))
                 return false;
         } else {
+            // Note: pn_body is NULL for functions which are being lazily parsed.
             JS_ASSERT(pn->getKind() == PNK_FUNCTION);
-            if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
-                return false;
+            if (pn->pn_body) {
+                if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
+                    return false;
+            }
         }
         break;
 
       case PN_LIST:
       {
         /* Propagate inCond through logical connectives. */
         bool cond = inCond && (pn->isKind(PNK_OR) || pn->isKind(PNK_AND));
 
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -10,16 +10,23 @@
 #include "mozilla/PodOperations.h"
 
 #include "ParseNode.h"
 #include "SharedContext.h"
 
 namespace js {
 namespace frontend {
 
+template <typename ParseHandler>
+class Parser;
+
+class SyntaxParseHandler;
+
+// Parse handler used when generating a full parse tree for all code which the
+// parser encounters.
 class FullParseHandler
 {
     ParseNodeAllocator allocator;
     TokenStream &tokenStream;
     bool foldConstants;
 
     ParseNode *allocParseNode(size_t size) {
         JS_ASSERT(size == sizeof(ParseNode));
@@ -29,45 +36,66 @@ class FullParseHandler
     ParseNode *cloneNode(const ParseNode &other) {
         ParseNode *node = allocParseNode(sizeof(ParseNode));
         if (!node)
             return NULL;
         mozilla::PodAssign(node, &other);
         return node;
     }
 
+    /*
+     * If this is a full parse to construct the bytecode for a function that
+     * was previously lazily parsed, that lazy function and the current index
+     * into its inner functions. We do not want to reparse the inner functions.
+     */
+    LazyScript * const lazyOuterFunction_;
+    size_t lazyInnerFunctionIndex;
+
   public:
+
+    /*
+     * If non-NULL, points to a syntax parser which can be used for inner
+     * functions. Cleared if language features not handled by the syntax parser
+     * are encountered, in which case all future activity will use the full
+     * parser.
+     */
+    Parser<SyntaxParseHandler> *syntaxParser;
+
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode *Node;
     typedef Definition *DefinitionNode;
 
-    FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
+    FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
+                     Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
       : allocator(cx),
         tokenStream(tokenStream),
-        foldConstants(foldConstants)
+        foldConstants(foldConstants),
+        lazyOuterFunction_(lazyOuterFunction),
+        lazyInnerFunctionIndex(0),
+        syntaxParser(syntaxParser)
     {}
 
     static ParseNode *null() { return NULL; }
 
     ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
     const Token &currentToken() { return tokenStream.currentToken(); }
 
     ParseNode *newName(PropertyName *name, ParseContext<FullParseHandler> *pc,
                        ParseNodeKind kind = PNK_NAME) {
         ParseNode *pn = NameNode::create(kind, name, this, pc);
         if (!pn)
             return NULL;
         pn->setOp(JSOP_NAME);
         return pn;
     }
-    Definition *newPlaceholder(ParseNode *pn, ParseContext<FullParseHandler> *pc) {
-        Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, this, pc);
+    Definition *newPlaceholder(JSAtom *atom, ParseContext<FullParseHandler> *pc) {
+        Definition *dn = (Definition *) NameNode::create(PNK_NAME, atom, this, pc);
         if (!dn)
             return NULL;
 
         dn->setOp(JSOP_NOP);
         dn->setDefn(true);
         dn->pn_dflags |= PND_PLACEHOLDER;
         return dn;
     }
@@ -308,16 +336,27 @@ class FullParseHandler
         return uintptr_t(dn);
     }
     static Definition *definitionFromBits(uintptr_t bits) {
         return (Definition *) bits;
     }
     static Definition *nullDefinition() {
         return NULL;
     }
+    void disableSyntaxParser() {
+        syntaxParser = NULL;
+    }
+
+    LazyScript *lazyOuterFunction() {
+        return lazyOuterFunction_;
+    }
+    JSFunction *nextLazyInnerFunction() {
+        JS_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
+        return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
+    }
 };
 
 inline bool
 FullParseHandler::addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
                                 ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody)
 {
     ParseNode *catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody);
     if (!catchpn)
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -627,16 +627,17 @@ struct ParseNode {
                                            still valid, but this use no longer
                                            optimizable via an upvar opcode */
 #define PND_CLOSED              0x80    /* variable is closed over */
 #define PND_DEFAULT            0x100    /* definition is an arg with a default */
 #define PND_IMPLICITARGUMENTS  0x200    /* the definition is a placeholder for
                                            'arguments' that has been converted
                                            into a definition after the function
                                            body has been parsed. */
+#define PND_EMITTEDFUNCTION    0x400    /* hoisted function that was emitted */
 
 /* Flags to propagate from uses to definition. */
 #define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
 
 /* PN_LIST pn_xflags bits. */
 #define PNX_STRCAT      0x01            /* PNK_ADD list has string term */
 #define PNX_CANTFOLD    0x02            /* PNK_ADD list has unfoldable term */
 #define PNX_POPVAR      0x04            /* PNK_VAR or PNK_CONST last result
--- a/js/src/frontend/Parser-inl.h
+++ b/js/src/frontend/Parser-inl.h
@@ -57,16 +57,17 @@ ParseContext<ParseHandler>::ParseContext
     decls_(prs->context),
     args_(prs->context),
     vars_(prs->context),
     yieldOffset(0),
     parserPC(&prs->pc),
     oldpc(prs->pc),
     lexdeps(prs->context),
     funcStmts(NULL),
+    innerFunctions(prs->context),
     inDeclDestructuring(false),
     funBecameStrict(false)
 {
     prs->pc = this;
 }
 
 template <typename ParseHandler>
 inline bool
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -54,16 +54,17 @@
 #include "frontend/Parser-inl.h"
 #include "frontend/SharedContext-inl.h"
 
 #include "vm/NumericConversions.h"
 #include "vm/RegExpObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
+using mozilla::Maybe;
 
 namespace js {
 namespace frontend {
 
 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 
 typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
@@ -208,16 +209,20 @@ bool
 ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name, Node pn,
                                          Definition::Kind kind)
 {
     JS_ASSERT(!decls_.lookupFirst(name));
 
     if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
         lexdeps->remove(name);
 
+    // Keep track of the number of arguments in args_, for fun->nargs.
+    if (kind == Definition::ARG && !args_.append((Definition *) NULL))
+        return false;
+
     return decls_.addUnique(name, kind);
 }
 
 template <typename ParseHandler>
 void
 ParseContext<ParseHandler>::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl)
 {
     JS_ASSERT(decls_.lookupFirst(name) == prevDecl);
@@ -373,33 +378,60 @@ Parser<ParseHandler>::reportWithOffset(P
 {
     va_list args;
     va_start(args, errorNumber);
     bool result = reportHelper(kind, strict, offset, errorNumber, args);
     va_end(args);
     return result;
 }
 
+template <>
+bool
+Parser<FullParseHandler>::abortIfSyntaxParser()
+{
+    handler.disableSyntaxParser();
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::abortIfSyntaxParser()
+{
+    abortedSyntaxParse = true;
+    return false;
+}
+
 template <typename ParseHandler>
 Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
-                             const jschar *chars, size_t length, bool foldConstants)
+                             const jschar *chars, size_t length, bool foldConstants,
+                             Parser<SyntaxParseHandler> *syntaxParser,
+                             LazyScript *lazyOuterFunction)
   : AutoGCRooter(cx, PARSER),
     context(cx),
     tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms),
     traceListHead(NULL),
     pc(NULL),
     sct(NULL),
     keepAtoms(cx->runtime),
     foldConstants(foldConstants),
     compileAndGo(options.compileAndGo),
     selfHostingMode(options.selfHostingMode),
-    unknownResult(false),
-    handler(cx, tokenStream, foldConstants)
-{
+    abortedSyntaxParse(false),
+    handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
+{
+    // XXX bug 678037 always disable syntax parsing for now.
+    handler.disableSyntaxParser();
+
     cx->activeCompilations++;
+
+    // The Mozilla specific 'strict' 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->hasStrictOption())
+        handler.disableSyntaxParser();
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::init()
 {
     if (!context->ensureParseMapPool())
         return false;
@@ -979,16 +1011,28 @@ Parser<FullParseHandler>::checkFunctionA
 
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkFunctionArguments()
 {
+    if (pc->sc->asFunctionBox()->function()->hasRest()) {
+        if (pc->lexdeps->lookup(context->names().arguments)) {
+            report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
+            return false;
+        }
+        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;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType type)
 {
     JS_ASSERT(pc->sc->isFunctionBox());
@@ -1218,16 +1262,59 @@ MatchOrInsertSemicolon(JSContext *cx, To
         ts->getToken(TSF_OPERAND);
         ts->reportError(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
     (void) ts->matchToken(TOK_SEMI);
     return true;
 }
 
+template <typename ParseHandler>
+typename ParseHandler::DefinitionNode
+Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
+{
+    AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
+    if (p)
+        return p.value().get<ParseHandler>();
+
+    DefinitionNode dn = handler.newPlaceholder(atom, pc);
+    if (!dn)
+        return ParseHandler::nullDefinition();
+    DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
+    if (!pc->lexdeps->add(p, atom, def))
+        return ParseHandler::nullDefinition();
+    return dn;
+}
+
+static bool
+ConvertDefinitionToNamedLambdaUse(JSContext *cx, ParseContext<FullParseHandler> *pc,
+                                  FunctionBox *funbox, Definition *dn)
+{
+    dn->setOp(JSOP_CALLEE);
+    if (!dn->pn_cookie.set(cx, pc->staticLevel, UpvarCookie::CALLEE_SLOT))
+        return false;
+    dn->pn_dflags |= PND_BOUND;
+    JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
+
+    /*
+     * Since 'dn' is a placeholder, it has not been defined in the
+     * ParseContext and hence we must manually flag a closed-over
+     * callee name as needing a dynamic scope (this is done for all
+     * definitions in the ParseContext by generateFunctionBindings).
+     *
+     * If 'dn' has been assigned to, then we also flag the function
+     * scope has needing a dynamic scope so that dynamic scope
+     * setter can either ignore the set (in non-strict mode) or
+     * produce an error (in strict mode).
+     */
+    if (dn->isClosed() || dn->isAssigned())
+        funbox->function()->setIsHeavyweight();
+    return true;
+}
+
 /*
  * Beware: this function is called for functions nested in other functions or
  * global scripts but not for functions compiled through the Function
  * constructor or JSAPI. To always execute code when a function has finished
  * parsing, use Parser::functionBody.
  */
 template <>
 bool
@@ -1246,36 +1333,18 @@ Parser<FullParseHandler>::leaveFunction(
     /* Propagate unresolved lexical names up to outerpc->lexdeps. */
     if (pc->lexdeps->count()) {
         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
             Definition *dn = r.front().value().get<FullParseHandler>();
             JS_ASSERT(dn->isPlaceholder());
 
             if (atom == funName && kind == Expression) {
-                dn->setOp(JSOP_CALLEE);
-                if (!dn->pn_cookie.set(context, pc->staticLevel,
-                                       UpvarCookie::CALLEE_SLOT))
+                if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
                     return false;
-                dn->pn_dflags |= PND_BOUND;
-                JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
-
-                /*
-                 * Since 'dn' is a placeholder, it has not been defined in the
-                 * ParseContext and hence we must manually flag a closed-over
-                 * callee name as needing a dynamic scope (this is done for all
-                 * definitions in the ParseContext by generateFunctionBindings).
-                 *
-                 * If 'dn' has been assigned to, then we also flag the function
-                 * scope has needing a dynamic scope so that dynamic scope
-                 * setter can either ignore the set (in non-strict mode) or
-                 * produce an error (in strict mode).
-                 */
-                if (dn->isClosed() || dn->isAssigned())
-                    funbox->function()->setIsHeavyweight();
                 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.
              */
@@ -1288,48 +1357,40 @@ Parser<FullParseHandler>::leaveFunction(
              * 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)
                 handler.deoptimizeUsesWithin(dn, fn->pn_pos);
 
             if (!outer_dn) {
-                AtomDefnAddPtr p = outerpc->lexdeps->lookupForAdd(atom);
-                if (p) {
-                    outer_dn = p.value().get<FullParseHandler>();
-                } else {
-                    /*
-                     * Create a new placeholder for our outer lexdep. We could
-                     * simply re-use the inner placeholder, but that introduces
-                     * subtleties in the case where we find a later definition
-                     * that captures an existing lexdep. For example:
-                     *
-                     *   function f() { function g() { x; } let x; }
-                     *
-                     * Here, g's TOK_UPVARS node lists the placeholder for x,
-                     * which must be captured by the 'let' declaration later,
-                     * since 'let's are hoisted.  Taking g's placeholder as our
-                     * own would work fine. But consider:
-                     *
-                     *   function f() { x; { function g() { x; } let x; } }
-                     *
-                     * Here, the 'let' must not capture all the uses of f's
-                     * lexdep entry for x, but it must capture the x node
-                     * referred to from g's TOK_UPVARS node.  Always turning
-                     * inherited lexdeps into uses of a new outer definition
-                     * allows us to handle both these cases in a natural way.
-                     */
-                    outer_dn = handler.newPlaceholder(dn, outerpc);
-                    if (!outer_dn)
-                        return false;
-                    DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(outer_dn);
-                    if (!outerpc->lexdeps->add(p, atom, def))
-                        return false;
-                }
+                /*
+                 * Create a new placeholder for our outer lexdep. We could
+                 * simply re-use the inner placeholder, but that introduces
+                 * subtleties in the case where we find a later definition
+                 * that captures an existing lexdep. For example:
+                 *
+                 *   function f() { function g() { x; } let x; }
+                 *
+                 * Here, g's TOK_UPVARS node lists the placeholder for x,
+                 * which must be captured by the 'let' declaration later,
+                 * since 'let's are hoisted.  Taking g's placeholder as our
+                 * own would work fine. But consider:
+                 *
+                 *   function f() { x; { function g() { x; } let x; } }
+                 *
+                 * Here, the 'let' must not capture all the uses of f's
+                 * lexdep entry for x, but it must capture the x node
+                 * referred to from g's TOK_UPVARS node.  Always turning
+                 * inherited lexdeps into uses of a new outer definition
+                 * allows us to handle both these cases in a natural way.
+                 */
+                outer_dn = getOrCreateLexicalDependency(outerpc, atom);
+                if (!outer_dn)
+                    return false;
             }
 
             /*
              * Insert dn's uses list at the front of outer_dn's list.
              *
              * Without loss of generality or correctness, we allow a dn to
              * be in inner and outer lexdeps, since the purpose of lexdeps
              * is one-pass coordination of name use and definition across
@@ -1358,31 +1419,29 @@ Parser<FullParseHandler>::leaveFunction(
 
             /* Mark the outer dn as escaping. */
             outer_dn->pn_dflags |= PND_CLOSED;
         }
     }
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    if (!pc->generateFunctionBindings(context, bindings))
-        return false;
-
-    pc->lexdeps.releaseMap(context);
-    return true;
+    return pc->generateFunctionBindings(context, bindings);
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
                                           ParseContext<SyntaxParseHandler> *outerpc,
                                           FunctionSyntaxKind kind)
 {
-    pc->lexdeps.releaseMap(context);
-    return true;
+    outerpc->blockidGen = pc->blockidGen;
+
+    FunctionBox *funbox = pc->sc->asFunctionBox();
+    return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
 }
 
 /*
  * defineArg is called for both the arguments of a regular function definition
  * and the arguments specified by the Function constructor.
  *
  * The 'disallowDuplicateArgs' bool indicates whether the use of another
  * feature (destructuring or default arguments) disables duplicate arguments.
@@ -1481,17 +1540,17 @@ Parser<ParseHandler>::functionArguments(
         if (tokenStream.getToken() != TOK_LP) {
             report(ParseError, false, null(),
                    kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         // Record the start of function source (for FunctionToString). If we
         // are parenFreeArrow, we will set this below, after consuming the NAME.
-        funbox->bufStart = tokenStream.currentToken().pos.begin;
+        funbox->setStart(tokenStream);
     }
 
     hasRest = false;
 
     Node argsbody = handler.newList(PNK_ARGSBODY);
     if (!argsbody)
         return false;
     handler.setFunctionBody(funcpn, argsbody);
@@ -1582,17 +1641,17 @@ Parser<ParseHandler>::functionArguments(
                     return false;
                 }
                 /* Fall through */
               }
 
               case TOK_NAME:
               {
                 if (parenFreeArrow)
-                    funbox->bufStart = tokenStream.currentToken().pos.begin;
+                    funbox->setStart(tokenStream);
 
                 RootedPropertyName name(context, tokenStream.currentToken().name());
                 bool disallowDuplicateArgs = destructuringArg || hasDefaults;
                 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
                     return false;
 
                 if (tokenStream.matchToken(TOK_ASSIGN)) {
                     // A default argument without parentheses would look like:
@@ -1638,19 +1697,21 @@ Parser<ParseHandler>::functionArguments(
     }
 
     return true;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
-                                                  ParseNode **pn_, FunctionSyntaxKind kind)
+                                                  ParseNode **pn_, FunctionSyntaxKind kind,
+                                                  bool *pbodyProcessed)
 {
     ParseNode *&pn = *pn_;
+    *pbodyProcessed = false;
 
     /* Function statements add a binding to the enclosing scope. */
     bool bodyLevel = pc->atBodyLevel();
 
     if (kind == Statement) {
         /*
          * Handle redeclaration and optimize cases where we can statically bind the
          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
@@ -1707,17 +1768,16 @@ Parser<FullParseHandler>::checkFunctionD
                 return false;
         }
 
         /*
          * As a SpiderMonkey-specific extension, non-body-level function
          * statements (e.g., functions in an "if" or "while" block) are
          * dynamically bound when control flow reaches the statement. The
          * emitter normally emits functions in two passes (see PNK_ARGSBODY).
-         * To distinguish
          */
         if (bodyLevel) {
             JS_ASSERT(pn->functionIsHoisted());
             JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
             JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
         } else {
             JS_ASSERT(!pc->sc->strict);
             JS_ASSERT(pn->pn_cookie.isFree());
@@ -1754,24 +1814,96 @@ Parser<FullParseHandler>::checkFunctionD
 
         /* No further binding (in BindNameToSlot) is needed for functions. */
         pn->pn_dflags |= PND_BOUND;
     } else {
         /* A function expression does not introduce any binding. */
         pn->setOp(JSOP_LAMBDA);
     }
 
+    // When a lazily-parsed function is called, we only fully parse (and emit)
+    // that function, not any of its nested children. The initial syntax-only
+    // parse recorded the free variables of nested functions and their extents,
+    // so we can skip over them after accounting for their free variables.
+    if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
+        JSFunction *fun = handler.nextLazyInnerFunction();
+        FunctionBox *funbox = newFunctionBox(fun, pc, /* strict = */ false);
+        if (!funbox)
+            return false;
+        handler.setFunctionBox(pn, funbox);
+
+        if (!addFreeVariablesFromLazyFunction(fun, pc))
+            return false;
+
+        // The position passed to tokenStream.advance() is relative to
+        // userbuf.base() while LazyScript::{begin,end} offsets are relative to
+        // the outermost script source. N.B: userbuf.base() is initialized
+        // (in TokenStream()) to begin() - column() so that column numbers in
+        // the lazily parsed script are correct.
+        uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
+        tokenStream.advance(fun->lazyScript()->end() - userbufBase);
+
+        *pbodyProcessed = true;
+        return true;
+    }
+
+    return true;
+}
+
+template <class T, class U>
+static inline void
+PropagateTransitiveParseFlags(const T *inner, U *outer)
+{
+   if (inner->bindingsAccessedDynamically())
+     outer->setBindingsAccessedDynamically();
+   if (inner->hasDebuggerStatement())
+     outer->setHasDebuggerStatement();
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
+                                                       ParseContext<ParseHandler> *pc)
+{
+    // Update any definition nodes in this context according to free variables
+    // in a lazily parsed inner function.
+
+    LazyScript *lazy = fun->lazyScript();
+    HeapPtrAtom *freeVariables = lazy->freeVariables();
+    for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
+        JSAtom *atom = freeVariables[i];
+
+        // 'arguments' will be implicitly bound within the inner function.
+        if (atom == context->names().arguments)
+            continue;
+
+        DefinitionNode dn = pc->decls().lookupFirst(atom);
+
+        if (!dn) {
+            dn = getOrCreateLexicalDependency(pc, atom);
+            if (!dn)
+                return false;
+        }
+
+        /* Mark the outer dn as escaping. */
+        handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
+    }
+
+    PropagateTransitiveParseFlags(lazy, pc->sc);
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
-                                                    Node *pn, FunctionSyntaxKind kind)
-{
+                                                    Node *pn, FunctionSyntaxKind kind,
+                                                    bool *pbodyProcessed)
+{
+    *pbodyProcessed = false;
+
     /* Function statements add a binding to the enclosing scope. */
     bool bodyLevel = pc->atBodyLevel();
 
     if (kind == Statement) {
         /*
          * Handle redeclaration and optimize cases where we can statically bind the
          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
          */
@@ -1792,46 +1924,55 @@ Parser<SyntaxParseHandler>::checkFunctio
             if (!pc->define(context, funName, *pn, Definition::VAR))
                 return false;
         }
 
         if (!bodyLevel && funName == context->names().arguments)
             pc->sc->setBindingsAccessedDynamically();
     }
 
+    if (kind == Arrow) {
+        /* Arrow functions cannot yet be parsed lazily. */
+        return abortIfSyntaxParser();
+    }
+
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
                                   size_t startOffset, FunctionType type, FunctionSyntaxKind kind)
 {
     JS_ASSERT_IF(kind == Statement, funName);
 
     /* Make a TOK_FUNCTION node. */
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         return null();
 
-    if (!checkFunctionDefinition(funName, &pn, kind))
+    bool bodyProcessed;
+    if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
         return null();
 
+    if (bodyProcessed)
+        return pn;
+
     RootedFunction fun(context, newFunction(pc, funName, kind));
     if (!fun)
         return null();
 
     // If the outer scope is strict, immediately parse the function in strict
     // mode. Otherwise, we parse it normally. If we see a "use strict"
     // directive, we backup and reparse it as strict.
     handler.setFunctionBody(pn, null());
     bool initiallyStrict = kind == Arrow || pc->sc->strict;
     bool becameStrict;
-    if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict,
-                             &becameStrict))
+    if (!functionArgsAndBody(pn, fun, funName, startOffset,
+                             type, kind, initiallyStrict, &becameStrict))
     {
         if (initiallyStrict || !becameStrict || tokenStream.hadError())
             return null();
 
         // Reparse the function in strict mode.
         tokenStream.seek(start);
         if (funName && tokenStream.getToken() == TOK_ERROR)
             return null();
@@ -1841,32 +1982,20 @@ Parser<ParseHandler>::functionDef(Handle
     }
 
     return pn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
-                                                   ParseNode *prelude, ParseNode *body,
-                                                   ParseContext<FullParseHandler> *outerpc)
+                                                   ParseNode *prelude, ParseNode *body)
 {
     pn->pn_pos.end = tokenStream.currentToken().pos.end;
 
-    /*
-     * Fruit of the poisonous tree: if a closure contains a dynamic name access
-     * (eval, with, etc), we consider the parent to do the same. The reason is
-     * that the deoptimizing effects of dynamic name access apply equally to
-     * parents: any local can be read at runtime.
-     */
-    if (funbox->bindingsAccessedDynamically())
-        outerpc->sc->setBindingsAccessedDynamically();
-    if (funbox->hasDebuggerStatement())
-        outerpc->sc->setHasDebuggerStatement();
-
 #if JS_HAS_DESTRUCTURING
     /*
      * If there were destructuring formal parameters, prepend the initializing
      * comma expression that we synthesized to body. If the body is a return
      * node, we must make a special PNK_SEQ node, to prepend the destructuring
      * code without bracing the decompilation of the function body.
      */
     if (prelude) {
@@ -1895,57 +2024,244 @@ Parser<FullParseHandler>::finishFunction
         ++body->pn_count;
         body->pn_xflags |= PNX_DESTRUCT;
     }
 #endif
 
     pn->pn_funbox = funbox;
     pn->pn_body->append(body);
     pn->pn_body->pn_pos = body->pn_pos;
-    pn->pn_blockid = outerpc->blockid();
 
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
-                                                     Node prelude, Node body,
-                                                     ParseContext<SyntaxParseHandler> *outerpc)
-{
+                                                     Node prelude, Node body)
+{
+    // The LazyScript for a lazily parsed function needs to be constructed
+    // while its ParseContext and associated lexdeps and inner functions are
+    // 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,
+                                          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())
+        freeVariables[i++].init(r.front().key());
+    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();
+    PropagateTransitiveParseFlags(funbox, lazy);
+
+    funbox->object->toFunction()->initLazyScript(lazy);
     return true;
 }
 
-template <typename ParseHandler>
+template <>
 bool
-Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
-                                          size_t startOffset, FunctionType type,
-                                          FunctionSyntaxKind kind, bool strict, bool *becameStrict)
+Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
+                                              HandlePropertyName funName,
+                                              size_t startOffset, FunctionType type,
+                                              FunctionSyntaxKind kind,
+                                              bool strict, bool *becameStrict)
 {
     if (becameStrict)
         *becameStrict = false;
-    ParseContext<ParseHandler> *outerpc = pc;
+    ParseContext<FullParseHandler> *outerpc = pc;
 
     // 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<ParseHandler> funpc(this, pc, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
+    // Disable lazy parsing if any functions are defined within a scope
+    // statement. Free names in the inner functions will be bound incorrectly.
+    if (pc->topScopeStmt)
+        handler.disableSyntaxParser();
+
+    // Try a syntax parse for this inner function.
+    do {
+        Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
+        if (!parser)
+            break;
+
+        {
+            // Move the syntax parser to the current position in the stream.
+            TokenStream::Position position(keepAtoms);
+            tokenStream.tell(&position);
+            parser->tokenStream.seek(position, tokenStream);
+
+            ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox,
+                                                   outerpc->staticLevel + 1, outerpc->blockidGen);
+            if (!funpc.init())
+                return false;
+
+            if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
+                                                    fun, funName, type, kind, strict, becameStrict))
+            {
+                if (parser->hadAbortedSyntaxParse()) {
+                    // Try again with a full parse.
+                    parser->clearAbortedSyntaxParse();
+                    break;
+                }
+                return false;
+            }
+
+            outerpc->blockidGen = funpc.blockidGen;
+
+            // Advance this parser over tokens processed by the syntax parser.
+            parser->tokenStream.tell(&position);
+            tokenStream.seek(position, parser->tokenStream);
+        }
+
+        pn->pn_funbox = funbox;
+
+        if (!addFreeVariablesFromLazyFunction(fun, pc))
+            return false;
+
+        pn->pn_blockid = outerpc->blockid();
+        PropagateTransitiveParseFlags(funbox, outerpc->sc);
+        return true;
+    } while (false);
+
+    // Continue doing a full parse for this inner function.
+    ParseContext<FullParseHandler> funpc(this, pc, funbox,
+                                         outerpc->staticLevel + 1, outerpc->blockidGen);
     if (!funpc.init())
         return false;
 
-    // Now parse formal argument list and compute fun->nargs.
+    if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
+        return false;
+
+    if (!leaveFunction(pn, funName, outerpc, kind))
+        return false;
+
+    pn->pn_blockid = outerpc->blockid();
+
+    /*
+     * Fruit of the poisonous tree: if a closure contains a dynamic name access
+     * (eval, with, etc), we consider the parent to do the same. The reason is
+     * that the deoptimizing effects of dynamic name access apply equally to
+     * parents: any local can be read at runtime.
+     */
+    PropagateTransitiveParseFlags(funbox, outerpc->sc);
+    return true;
+}
+
+template <>
+bool
+Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
+                                                HandlePropertyName funName,
+                                                size_t startOffset, FunctionType type,
+                                                FunctionSyntaxKind kind,
+                                                bool strict, bool *becameStrict)
+{
+    if (becameStrict)
+        *becameStrict = false;
+    ParseContext<SyntaxParseHandler> *outerpc = pc;
+
+    // Create box for fun->object early to protect against last-ditch GC.
+    FunctionBox *funbox = newFunctionBox(fun, pc, strict);
+    if (!funbox)
+        return false;
+
+    // Initialize early for possible flags mutation via destructuringExpr.
+    ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
+                                           outerpc->staticLevel + 1, outerpc->blockidGen);
+    if (!funpc.init())
+        return false;
+
+    if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
+        return false;
+
+    if (!leaveFunction(pn, funName, outerpc, kind))
+        return false;
+
+    // This is a lazy function inner to another lazy function. Remember the
+    // inner function so that if the outer function is eventually parsed we do
+    // not need any further parsing or processing of the inner function.
+    JS_ASSERT(fun->lazyScript());
+    return outerpc->innerFunctions.append(fun);
+}
+
+template <>
+ParseNode *
+Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
+                                                 bool strict)
+{
+    Node pn = handler.newFunctionDefinition();
+    if (!pn)
+        return null();
+
+    FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
+    if (!funbox)
+        return null();
+    handler.setFunctionBox(pn, funbox);
+
+    ParseContext<FullParseHandler> funpc(this, NULL, funbox, staticLevel, 0);
+    if (!funpc.init())
+        return null();
+
+    RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
+
+    if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
+        return null();
+
+    if (fun->isNamedLambda()) {
+        if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
+            Definition *dn = p.value().get<FullParseHandler>();
+            if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
+                return NULL;
+        }
+    }
+
+    InternalHandle<Bindings*> bindings =
+        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
+    if (!pc->generateFunctionBindings(context, bindings))
+        return null();
+
+    return pn;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
+                                                 HandlePropertyName funName, FunctionType type,
+                                                 FunctionSyntaxKind kind,
+                                                 bool strict, bool *becameStrict)
+{
+    // Given a properly initialized parse context, try to parse an actual
+    // function without concern for conversion to strict mode, use of lazy
+    // parsing and such.
+
     Node prelude = null();
     bool hasRest;
     if (!functionArguments(kind, &prelude, pn, hasRest))
         return false;
 
-    fun->setArgCount(funpc.numArgs());
+    FunctionBox *funbox = pc->sc->asFunctionBox();
+
+    fun->setArgCount(pc->numArgs());
     if (funbox->ndefaults)
         fun->setHasDefaults();
     if (hasRest)
         fun->setHasRest();
 
     if (type == Getter && fun->nargs > 0) {
         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
         return false;
@@ -1956,17 +2272,17 @@ Parser<ParseHandler>::functionArgsAndBod
     }
 
     if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) {
         report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
         return false;
     }
 
     // Parse the function body.
-    mozilla::Maybe<GenexpGuard<ParseHandler> > yieldGuard;
+    Maybe<GenexpGuard<ParseHandler> > yieldGuard;
     if (kind == Arrow)
         yieldGuard.construct(this);
 
     FunctionBodyType bodyType = StatementListBody;
     if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
         tokenStream.ungetToken();
         bodyType = ExpressionBody;
         fun->setIsExprClosure();
@@ -1999,20 +2315,17 @@ Parser<ParseHandler>::functionArgsAndBod
         if (tokenStream.hadError())
             return false;
         funbox->bufEnd = tokenStream.currentToken().pos.end;
         if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
             return false;
     }
 #endif
 
-    if (!finishFunctionDefinition(pn, funbox, prelude, body, outerpc))
-        return false;
-
-    return leaveFunction(pn, funName, outerpc, kind);
+    return finishFunctionDefinition(pn, funbox, prelude, body);
 }
 
 template <>
 ParseNode *
 Parser<FullParseHandler>::moduleDecl()
 {
     JS_ASSERT(tokenStream.currentToken().name() == context->runtime->atomState.module);
     if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
@@ -2045,17 +2358,17 @@ Parser<FullParseHandler>::moduleDecl()
 
     return pn;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::moduleDecl()
 {
-    setUnknownResult();
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionStmt()
 {
     JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
@@ -2174,16 +2487,18 @@ Parser<ParseHandler>::maybeParseDirectiv
                     }
                     pc->sc->strict = true;
                 }
             }
         } else if (directive == context->names().useAsm) {
             if (pc->sc->isFunctionBox()) {
                 pc->sc->asFunctionBox()->useAsm = true;
                 pc->sc->asFunctionBox()->asmStart = handler.getPosition(pn).begin;
+                if (!abortIfSyntaxParser())
+                    return false;
             } else {
                 if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL))
                     return false;
             }
         }
     }
     return true;
 }
@@ -2581,34 +2896,27 @@ Parser<ParseHandler>::noteNameUse(Handle
     StmtInfoPC *stmt = LexicalLookup(pc, name, NULL, (StmtInfoPC *)NULL);
 
     DefinitionList::Range defs = pc->decls().lookupMulti(name);
 
     DefinitionNode dn;
     if (!defs.empty()) {
         dn = defs.front<ParseHandler>();
     } else {
-        if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) {
-            dn = p.value().get<ParseHandler>();
-        } else {
-            /*
-             * No definition before this use in any lexical scope.
-             * Create a placeholder definition node to either:
-             * - Be adopted when we parse the real defining
-             *   declaration, or
-             * - Be left as a free variable definition if we never
-             *   see the real definition.
-             */
-            dn = handler.newPlaceholder(pn, pc);
-            if (!dn)
-                return false;
-            DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
-            if (!pc->lexdeps->add(p, name, def))
-                return false;
-        }
+        /*
+         * No definition before this use in any lexical scope.
+         * Create a placeholder definition node to either:
+         * - Be adopted when we parse the real defining
+         *   declaration, or
+         * - Be left as a free variable definition if we never
+         *   see the real definition.
+         */
+        dn = getOrCreateLexicalDependency(pc, name);
+        if (!dn)
+            return false;
     }
 
     handler.linkUseToDef(pn, dn);
 
     if (stmt && stmt->type == STMT_WITH)
         handler.setFlag(pn, PND_DEOPTIMIZED);
 
     return true;
@@ -2842,18 +3150,17 @@ Parser<FullParseHandler>::checkDestructu
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
                                                Node left, bool toplevel)
 {
-    setUnknownResult();
-    return false;
+    return abortIfSyntaxParser();
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind tt)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
 
@@ -2884,16 +3191,19 @@ Parser<ParseHandler>::returnOrYield(bool
     JSOp op = (tt == TOK_RETURN) ? JSOP_RETURN : JSOP_YIELD;
 
     Node pn = handler.newUnary(kind, op);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATORS
     if (tt == TOK_YIELD) {
+        if (!abortIfSyntaxParser())
+            return null();
+
         /*
          * If we're within parens, we won't know if this is a generator expression until we see
          * a |for| token, so we have to delay flagging the current function.
          */
         if (pc->parenDepth == 0) {
             pc->sc->asFunctionBox()->setIsGenerator();
         } else {
             pc->yieldCount++;
@@ -3018,17 +3328,17 @@ Parser<FullParseHandler>::pushLetScope(H
 
     return pn;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
 {
-    setUnknownResult();
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 /*
  * Parse a let block statement or let expression (determined by 'letContext').
  * In both cases, bindings are not hoisted to the top of the enclosing block
  * and thus must be carefully injected between variables() and the let body.
  */
@@ -3396,16 +3706,17 @@ Parser<FullParseHandler>::forStatement()
             pc->parsingForInit = true;
             if (tt == TOK_VAR || tt == TOK_CONST) {
                 forDecl = true;
                 tokenStream.consumeKnownToken(tt);
                 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
             }
 #if JS_HAS_BLOCK_SCOPE
             else if (tt == TOK_LET) {
+                handler.disableSyntaxParser();
                 (void) tokenStream.getToken();
                 if (tokenStream.peekToken() == TOK_LP) {
                     pn1 = letBlock(LetExpresion);
                 } else {
                     forDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
                         return null();
@@ -3690,16 +4001,22 @@ Parser<SyntaxParseHandler>::forStatement
      * parsing of 'for' statements is thus done separately, and only handles
      * the types of 'for' statements likely to be seen in web content.
      */
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     StmtInfoPC forStmt(context);
     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
 
+    /* Don't parse 'for each' loops. */
+    if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
+        JS_ALWAYS_FALSE(abortIfSyntaxParser());
+        return null();
+    }
+
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     /* True if we have 'for (var ...)'. */
     bool forDecl = false;
     bool simpleForDecl = true;
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     Node lhsNode;
@@ -3713,17 +4030,17 @@ Parser<SyntaxParseHandler>::forStatement
             pc->parsingForInit = true;
             if (tt == TOK_VAR) {
                 forDecl = true;
                 tokenStream.consumeKnownToken(tt);
                 lhsNode = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, &simpleForDecl);
             }
 #if JS_HAS_BLOCK_SCOPE
             else if (tt == TOK_CONST || tt == TOK_LET) {
-                setUnknownResult();
+                JS_ALWAYS_FALSE(abortIfSyntaxParser());
                 return null();
             }
 #endif
             else {
                 lhsNode = expr();
             }
             if (!lhsNode)
                 return null();
@@ -3742,22 +4059,22 @@ Parser<SyntaxParseHandler>::forStatement
         /* 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::NodeLValue)
         {
-            setUnknownResult();
+            JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!simpleForDecl) {
-            setUnknownResult();
+            JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!forDecl && !setAssignmentLhsOps(lhsNode, JSOP_NOP))
             return null();
 
         if (!expr())
             return null();
@@ -3956,16 +4273,19 @@ Parser<ParseHandler>::tryStatement()
     handler.setEndPosition(pn, finallyBlock ? finallyBlock : catchList);
     return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::withStatement()
 {
+    if (!abortIfSyntaxParser())
+        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_STRICT 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
     // warning under JSOPTION_STRICT.  See
@@ -4012,16 +4332,18 @@ Parser<ParseHandler>::withStatement()
     return pn;
 }
 
 #if JS_HAS_BLOCK_SCOPE
 template <>
 ParseNode *
 Parser<FullParseHandler>::letStatement()
 {
+    handler.disableSyntaxParser();
+
     ParseNode *pn;
     do {
         /* Check for a let statement or let expression. */
         if (tokenStream.peekToken() == TOK_LP) {
             pn = letBlock(LetStatement);
             if (!pn)
                 return null();
 
@@ -4129,17 +4451,17 @@ Parser<FullParseHandler>::letStatement()
     /* Check termination of this primitive statement. */
     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::letStatement()
 {
-    setUnknownResult();
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 #endif // JS_HAS_BLOCK_SCOPE
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement()
@@ -4412,16 +4734,19 @@ Parser<ParseHandler>::statement()
         if (!pn)
             return null();
 
         /* Tell js_EmitTree to generate a final POP. */
         handler.setListFlag(pn, PNX_POPVAR);
         break;
 
       case TOK_CONST:
+        if (!abortIfSyntaxParser())
+            return null();
+
         pn = variables(PNK_CONST);
         if (!pn)
             return null();
 
         /* Tell js_EmitTree to generate a final POP. */
         handler.setListFlag(pn, PNX_POPVAR);
         break;
 
@@ -4868,20 +5193,18 @@ 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) {
-        setUnknownResult();
-        return false;
-    }
+    if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue)
+        return abortIfSyntaxParser();
     return checkStrictAssignment(pn);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr()
 {
     JS_CHECK_RECURSION(context, return null());
@@ -4914,16 +5237,18 @@ Parser<ParseHandler>::assignExpr()
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    break;
 
       case TOK_ARROW: {
         tokenStream.seek(start);
+        if (!abortIfSyntaxParser())
+            return null();
 
         if (tokenStream.getToken() == TOK_ERROR)
             return null();
         size_t offset = tokenStream.currentToken().pos.begin;
         tokenStream.ungetToken();
 
         return functionDef(NullPtr(), start, offset, Normal, Arrow);
       }
@@ -5049,16 +5374,22 @@ Parser<FullParseHandler>::checkDeleteExp
 
 template <>
 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())
+        return abortIfSyntaxParser();
+
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr()
 {
     Node pn, pn2;
@@ -5404,17 +5735,17 @@ CompExprTransplanter::transplant(ParseNo
                     /*
                      * The variable originally appeared to be a use of a
                      * definition or placeholder outside the generator, but now
                      * we know it is scoped within the comprehension tail's
                      * clauses. Make it (along with any other uses within the
                      * generator) a use of a new placeholder in the generator's
                      * lexdeps.
                      */
-                    Definition *dn2 = parser->handler.newPlaceholder(pn, parser->pc);
+                    Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc);
                     if (!dn2)
                         return false;
                     dn2->pn_pos = root->pn_pos;
 
                     /*
                      * Change all uses of |dn| that lie within the generator's
                      * |yield| expression into uses of dn2.
                      */
@@ -5486,16 +5817,28 @@ CompExprTransplanter::transplant(ParseNo
  * (possibly nested) for-loop, initialized by |kind, op, kid|.
  */
 template <>
 ParseNode *
 Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp,
                                             ParseContext<FullParseHandler> *outerpc,
                                             ParseNodeKind kind, JSOp op)
 {
+    /*
+     * If we saw any inner functions while processing the generator expression
+     * then they may have upvars referring to the let vars in this generator
+     * which were not correctly processed. Bail out and start over without
+     * allowing lazy parsing.
+     */
+    if (handler.syntaxParser) {
+        handler.disableSyntaxParser();
+        abortedSyntaxParse = true;
+        return NULL;
+    }
+
     unsigned adjust;
     ParseNode *pn, *pn2, *pn3, **pnp;
     StmtInfoPC stmtInfo(context);
     BindData<FullParseHandler> data(context);
     TokenKind tt;
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
 
@@ -5741,18 +6084,17 @@ Parser<FullParseHandler>::arrayInitializ
     pn->append(pntop);
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn)
 {
-    setUnknownResult();
-    return false;
+    return abortIfSyntaxParser();
 }
 
 #if JS_HAS_GENERATOR_EXPRS
 
 /*
  * Starting from a |for| keyword after an expression, parse the comprehension
  * tail completing this generator expression. Wrap the expression at kid in a
  * generator function that is immediately called to evaluate to the generator
@@ -5848,17 +6190,17 @@ Parser<FullParseHandler>::generatorExpr(
     result->initList(genfn);
     return result;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::generatorExpr(Node kid)
 {
-    setUnknownResult();
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 static const char js_generator_str[] = "generator";
 
 #endif /* JS_HAS_GENERATOR_EXPRS */
 #endif /* JS_HAS_GENERATORS */
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -35,16 +35,17 @@ struct StmtInfoPC : public StmtInfoBase 
 
     StmtInfoPC(JSContext *cx) : StmtInfoBase(cx) {}
 };
 
 typedef HashSet<JSAtom *> FuncStmtSet;
 class SharedContext;
 
 typedef Vector<Definition *, 16> DeclVector;
+typedef Vector<JSFunction *, 4> FunctionVector;
 
 struct GenericParseContext
 {
     // Enclosing function or global context.
     GenericParseContext *parent;
 
     // Context shared between parsing and bytecode generation.
     SharedContext *sc;
@@ -198,16 +199,19 @@ struct ParseContext : public GenericPars
 
   public:
     OwnedAtomDefnMapPtr lexdeps;    /* unresolved lexical name dependencies */
 
     FuncStmtSet     *funcStmts;     /* Set of (non-top-level) function statements
                                        that will alias any top-level bindings with
                                        the same name. */
 
+    // All inner functions in this context. Only filled in when parsing syntax.
+    FunctionVector innerFunctions;
+
     // Set when parsing a declaration-like destructuring pattern.  This flag
     // causes PrimaryExpr to create PN_NAME parse nodes for variable references
     // which are not hooked into any definition's use chain, added to any tree
     // context's AtomList, etc. etc.  CheckDestructuring will do that work
     // later.
     //
     // The comments atop CheckDestructuring explain the distinction between
     // assignment-like and declaration-like destructuring patterns, and why
@@ -249,16 +253,17 @@ struct BindData;
 
 class CompExprTransplanter;
 
 template <typename ParseHandler>
 class GenexpGuard;
 
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
+enum FunctionType { Getter, Setter, Normal };
 
 template <typename ParseHandler>
 struct Parser : private AutoGCRooter, public StrictModeGetter
 {
     JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
     TokenStream         tokenStream;
     LifoAlloc::Mark     tempPoolMark;
 
@@ -296,17 +301,17 @@ struct Parser : private AutoGCRooter, pu
      */
     const bool          selfHostingMode:1;
 
     /*
      * Not all language constructs can be handled during syntax parsing. If it
      * is not known whether the parse succeeds or fails, this bit is set and
      * the parse will return false.
      */
-    bool unknownResult;
+    bool abortedSyntaxParse;
 
     typedef typename ParseHandler::Node Node;
     typedef typename ParseHandler::DefinitionNode DefinitionNode;
 
   public:
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
 
@@ -314,17 +319,19 @@ struct Parser : private AutoGCRooter, pu
     bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
                       unsigned errorNumber, va_list args);
   public:
     bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
     bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
                           ...);
 
     Parser(JSContext *cx, const CompileOptions &options,
-           const jschar *chars, size_t length, bool foldConstants);
+           const jschar *chars, size_t length, bool foldConstants,
+           Parser<SyntaxParseHandler> *syntaxParser,
+           LazyScript *lazyOuterFunction);
     ~Parser();
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
     /*
      * Initialize a parser. The compiler owns the arena pool "tops-of-stack"
      * space above the current JSContext.tempLifoAlloc mark. This means you
      * cannot allocate from tempLifoAlloc and save the pointer beyond the next
@@ -351,51 +358,60 @@ struct Parser : private AutoGCRooter, pu
     /*
      * Create a new function object given parse context (pc) and a name (which
      * is optional if this is a function expression).
      */
     JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind);
 
     void trace(JSTracer *trc);
 
-    bool hadUnknownResult() {
-        return unknownResult;
+    bool hadAbortedSyntaxParse() {
+        return abortedSyntaxParse;
+    }
+    void clearAbortedSyntaxParse() {
+        abortedSyntaxParse = false;
     }
 
   private:
     Parser *thisForCtor() { return this; }
 
     /*
      * Create a parse node with the given kind and op using the current token's
-     * atom.
-     */
+     * atom. 
+    */
     Node atomNode(ParseNodeKind kind, JSOp op);
 
-    void setUnknownResult() {
-        unknownResult = true;
-    }
+    inline bool abortIfSyntaxParser();
 
   public:
 
     /* Public entry points for parsing. */
     Node statement();
     bool maybeParseDirective(Node pn, bool *cont);
 
     // Parse a function, given only its body. Used for the Function constructor.
     Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
                                 Node fn, FunctionBox **funbox, bool strict,
                                 bool *becameStrict = NULL);
 
+    // Parse a function, given only its arguments and body. Used for lazily
+    // parsed functions.
+    Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
+
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
      */
     enum FunctionBodyType { StatementListBody, ExpressionBody };
     Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
 
+    bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
+                                    HandlePropertyName funName, FunctionType type,
+                                    FunctionSyntaxKind kind, bool strict, bool *becameStrict);
+
     virtual bool strictMode() { return pc->sc->strict; }
 
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
@@ -434,17 +450,16 @@ struct Parser : private AutoGCRooter, pu
     Node unaryExpr();
     Node memberExpr(TokenKind tt, bool allowCallSyntax);
     Node primaryExpr(TokenKind tt);
     Node parenExpr(bool *genexp = NULL);
 
     /*
      * Additional JS parsers.
      */
-    enum FunctionType { Getter, Setter, Normal };
     bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
 
     Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
                      size_t startOffset, FunctionType type, FunctionSyntaxKind kind);
     bool functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
                              size_t startOffset, FunctionType type, FunctionSyntaxKind kind,
                              bool strict, bool *becameStrict = NULL);
 
@@ -473,20 +488,20 @@ struct Parser : private AutoGCRooter, pu
     }
 
     bool setAssignmentLhsOps(Node pn, JSOp op);
     bool matchInOrOf(bool *isForOfp);
 
     void addStatementToList(Node pn, Node kid, bool *hasFunctionStmt);
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
-    bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind);
-    bool finishFunctionDefinition(Node pn, FunctionBox *funbox,
-                                  Node prelude, Node body,
-                                  ParseContext<ParseHandler> *outerpc);
+    bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
+                                 bool *pbodyProcessed);
+    bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
+    bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version,
                                 bool forDecl, bool forEach, bool forOf);
     bool setLvalKid(Node pn, Node kid, const char *name);
     bool setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder);
     bool checkStrictAssignment(Node lhs);
     bool checkStrictBinding(HandlePropertyName name, Node pn);
     bool checkDeleteExpression(Node *pn);
@@ -518,16 +533,17 @@ struct Parser : private AutoGCRooter, pu
     bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data,
                    HandlePropertyName name, Parser<ParseHandler> *parser);
 
     static Node null() { return ParseHandler::null(); }
 
     bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
     bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
     bool checkFinalReturn(Node pn);
+    DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
 
     bool leaveFunction(Node fn, HandlePropertyName funName,
                        ParseContext<ParseHandler> *outerpc,
                        FunctionSyntaxKind kind = Expression);
 
     friend class CompExprTransplanter;
     friend class GenexpGuard<ParseHandler>;
     friend struct BindData<ParseHandler>;
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -12,16 +12,17 @@
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jsprvtd.h"
 #include "jspubtd.h"
 
 #include "builtin/Module.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/ParseNode.h"
+#include "frontend/TokenStream.h"
 #include "vm/ScopeObject.h"
 
 namespace js {
 namespace frontend {
 
 // These flags apply to both global and function contexts.
 class AnyContextFlags
 {
@@ -195,16 +196,18 @@ public:
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
     uint32_t        bufStart;
     uint32_t        bufEnd;
+    uint32_t        startLine;
+    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 */
 
     FunctionContextFlags funCxFlags;
@@ -229,16 +232,22 @@ class FunctionBox : public ObjectBox, pu
     void setDefinitelyNeedsArgsObj()       { JS_ASSERT(funCxFlags.argumentsHasLocalBinding);
                                              funCxFlags.definitelyNeedsArgsObj   = true; }
 
     // Return whether this function has either specified "use asm" or is
     // (transitively) nested inside a function that has.
     bool useAsmOrInsideUseAsm() const {
         return useAsm || insideUseAsm;
     }
+
+    void setStart(const TokenStream &tokenStream) {
+        bufStart = tokenStream.currentToken().pos.begin;
+        startLine = tokenStream.getLineno();
+        startColumn = tokenStream.getColumn();
+    }
 };
 
 inline FunctionBox *
 SharedContext::asFunctionBox()
 {
     JS_ASSERT(isFunctionBox());
     return static_cast<FunctionBox*>(this);
 }
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -5,49 +5,60 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SyntaxParseHandler_h__
 #define SyntaxParseHandler_h__
 
 namespace js {
 namespace frontend {
 
+// Parse handler used when processing the syntax in a block of code, to generate
+// the minimal information which is required to detect syntax errors and allow
+// bytecode to be emitted for outer functions.
+//
+// When parsing, we start at the top level with a full parse, and when possible
+// only check the syntax for inner functions, so that they can be lazily parsed
+// into bytecode when/if they first run. Checking the syntax of a function is
+// several times faster than doing a full parse/emit, and lazy parsing improves
+// both performance and memory usage significantly when pages contain large
+// amounts of code that never executes (which happens often).
 class SyntaxParseHandler
 {
-    /* Remember the last encountered name or string literal during syntax parses. */
+    // Remember the last encountered name or string literal during syntax parses.
     JSAtom *lastAtom;
     TokenPos lastStringPos;
     TokenStream &tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
         NodeName,
         NodeString,
         NodeStringExprStatement,
         NodeLValue
     };
     typedef Definition::Kind DefinitionNode;
 
-    SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
+    SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
+                       Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
       : lastAtom(NULL),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer *trc) {}
 
     Node newName(PropertyName *name, ParseContext<SyntaxParseHandler> *pc,
                  ParseNodeKind kind = PNK_NAME) {
         lastAtom = name;
         return NodeName;
     }
-    DefinitionNode newPlaceholder(Node pn, ParseContext<SyntaxParseHandler> *pc) {
+    DefinitionNode newPlaceholder(JSAtom *atom, ParseContext<SyntaxParseHandler> *pc) {
         return Definition::PLACEHOLDER;
     }
     Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
         if (kind == PNK_STRING) {
             lastAtom = atom;
             lastStringPos = tokenStream.currentToken().pos;
         }
         return NodeString;
@@ -177,14 +188,16 @@ class SyntaxParseHandler
         return uintptr_t(dn << 1);
     }
     static DefinitionNode definitionFromBits(uintptr_t bits) {
         return (DefinitionNode) (bits >> 1);
     }
     static DefinitionNode nullDefinition() {
         return Definition::MISSING;
     }
+    void disableSyntaxParser() {
+    }
 };
 
 } // namespace frontend
 } // namespace js
 
 #endif /* SyntaxParseHandler_h__ */
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -153,16 +153,32 @@ TokenStream::SourceCoords::add(uint32_t 
 
     } else {
         // We have seen this newline before (and ungot it).  Do nothing (other
         // than checking it hasn't mysteriously changed).
         JS_ASSERT(lineStartOffsets_[lineIndex] == lineStartOffset);
     }
 }
 
+JS_ALWAYS_INLINE void
+TokenStream::SourceCoords::fill(const TokenStream::SourceCoords &other)
+{
+    JS_ASSERT(lineStartOffsets_.back() == MAX_PTR);
+    JS_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
+
+    if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
+        return;
+
+    uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
+    lineStartOffsets_[sentinelIndex] = other.lineStartOffsets_[sentinelIndex];
+
+    for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++)
+        (void)lineStartOffsets_.append(other.lineStartOffsets_[i]);
+}
+
 JS_ALWAYS_INLINE uint32_t
 TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const
 {
     uint32_t iMin, iMax, iMid;
 
     if (lineStartOffsets_[lastLineIndex_] <= offset) {
         // If we reach here, offset is on a line the same as or higher than
         // last time.  Check first for the +0, +1, +2 cases, because they
@@ -246,33 +262,39 @@ TokenStream::TokenStream(JSContext *cx, 
                          const jschar *base, size_t length, StrictModeGetter *smg,
                          AutoKeepAtoms& keepAtoms)
   : srcCoords(cx, options.lineno),
     tokens(),
     cursor(),
     lookahead(),
     lineno(options.lineno),
     flags(),
-    linebase(base),
+    linebase(base - options.column),
     prevLinebase(NULL),
-    userbuf(cx, base, length),
+    userbuf(cx, base - options.column, length + options.column), // See comment below
     filename(options.filename),
     sourceMap(NULL),
     listenerTSData(),
     tokenbuf(cx),
     version(options.version),
     cx(cx),
     originPrincipals(JSScript::normalizeOriginPrincipals(options.principals,
                                                          options.originPrincipals)),
     strictModeGetter(smg),
     lastFunctionKeyword(keepAtoms),
     tokenSkip(cx, &tokens),
     linebaseSkip(cx, &linebase),
     prevLinebaseSkip(cx, &prevLinebase)
 {
+    // Column numbers are computed as offsets from the current line's base, so the
+    // initial line's base must be included in the buffer. linebase and userbuf
+    // were adjusted above, and if we are starting tokenization part way through
+    // this line then adjust the next character.
+    userbuf.setAddressOfNextRawChar(base);
+
     if (originPrincipals)
         JS_HoldPrincipals(originPrincipals);
 
     JSSourceHandler listener = cx->runtime->debugHooks.sourceHandler;
     void *listenerData = cx->runtime->debugHooks.sourceHandlerData;
 
     if (listener)
         listener(options.filename, options.lineno, base, length, &listenerTSData, listenerData);
@@ -497,16 +519,29 @@ TokenStream::TokenBuf::findEOLMax(const 
         if (TokenBuf::isRawEOLChar(*p++))
             break;
         n++;
     }
     return p;
 }
 
 void
+TokenStream::advance(size_t position)
+{
+    const jschar *end = userbuf.base() + position;
+    while (userbuf.addressOfNextRawChar() < end)
+        getChar();
+
+    Token *cur = &tokens[cursor];
+    cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
+    cur->type = TOK_ERROR;
+    lookahead = 0;
+}
+
+void
 TokenStream::tell(Position *pos)
 {
     pos->buf = userbuf.addressOfNextRawChar();
     pos->flags = flags;
     pos->lineno = lineno;
     pos->linebase = linebase;
     pos->prevLinebase = prevLinebase;
     pos->lookahead = lookahead;
@@ -526,16 +561,23 @@ TokenStream::seek(const Position &pos)
     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);
+    seek(pos);
+}
+
+void
 TokenStream::positionAfterLastFunctionKeyword(Position &pos)
 {
     JS_ASSERT(lastFunctionKeyword.buf > userbuf.base());
     PodAssign(&pos, &lastFunctionKeyword);
 }
 
 bool
 TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber,
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -467,16 +467,17 @@ class MOZ_STACK_CLASS TokenStream
     }
     bool isCurrentTokenType(TokenKind type1, TokenKind type2) const {
         TokenKind type = currentToken().type;
         return type == type1 || type == type2;
     }
     const CharBuffer &getTokenbuf() const { return tokenbuf; }
     const char *getFilename() const { return filename; }
     unsigned getLineno() const { return lineno; }
+    unsigned getColumn() const { return userbuf.addressOfNextRawChar() - linebase - 1; }
     JSVersion versionNumber() const { return VersionNumber(version); }
     JSVersion versionWithFlags() const { return version; }
     bool hadError() const { return !!(flags & TSF_HAD_ERROR); }
 
     bool isCurrentTokenEquality() const {
         return TokenKindIsEquality(currentToken().type);
     }
 
@@ -655,18 +656,20 @@ class MOZ_STACK_CLASS TokenStream
         unsigned lineno;
         const jschar *linebase;
         const jschar *prevLinebase;
         Token currentToken;
         unsigned lookahead;
         Token lookaheadTokens[maxLookahead];
     };
 
+    void advance(size_t position);
     void tell(Position *);
     void seek(const Position &pos);
+    void seek(const Position &pos, const TokenStream &other);
     void positionAfterLastFunctionKeyword(Position &pos);
 
     size_t positionToOffset(const Position &pos) const {
         return pos.buf - userbuf.base();
     }
 
     bool hasSourceMap() const {
         return sourceMap != NULL;
@@ -747,16 +750,17 @@ class MOZ_STACK_CLASS TokenStream
 
         uint32_t lineIndexToNum(uint32_t lineIndex) const { return lineIndex + initialLineNum_; }
         uint32_t lineNumToIndex(uint32_t lineNum)   const { return lineNum   - initialLineNum_; }
 
       public:
         SourceCoords(JSContext *cx, uint32_t ln);
 
         void add(uint32_t lineNum, uint32_t lineStartOffset);
+        void fill(const SourceCoords &other);
 
         bool isOnThisLine(uint32_t offset, uint32_t lineNum) const {
             uint32_t lineIndex = lineNumToIndex(lineNum);
             JS_ASSERT(lineIndex + 1 < lineStartOffsets_.length());  // +1 due to sentinel
             return lineStartOffsets_[lineIndex] <= offset &&
                    offset < lineStartOffsets_[lineIndex + 1];
         }
 
@@ -823,17 +827,17 @@ class MOZ_STACK_CLASS TokenStream
             return false;
         }
 
         void ungetRawChar() {
             JS_ASSERT(ptr);     /* make sure haven't been poisoned */
             ptr--;
         }
 
-        const jschar *addressOfNextRawChar() {
+        const jschar *addressOfNextRawChar() const {
             JS_ASSERT(ptr);     /* make sure haven't been poisoned */
             return ptr;
         }
 
         /* Use this with caution! */
         void setAddressOfNextRawChar(const jschar *a) {
             JS_ASSERT(a);
             ptr = a;
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -57,16 +57,17 @@ enum AllocKind {
     FINALIZE_OBJECT8,
     FINALIZE_OBJECT8_BACKGROUND,
     FINALIZE_OBJECT12,
     FINALIZE_OBJECT12_BACKGROUND,
     FINALIZE_OBJECT16,
     FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_SCRIPT,
+    FINALIZE_LAZY_SCRIPT,
     FINALIZE_SHAPE,
     FINALIZE_BASE_SHAPE,
     FINALIZE_TYPE_OBJECT,
     FINALIZE_SHORT_STRING,
     FINALIZE_STRING,
     FINALIZE_EXTERNAL_STRING,
     FINALIZE_IONCODE,
     FINALIZE_LAST = FINALIZE_IONCODE
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -76,16 +76,17 @@ PushMarkStack(GCMarker *gcmarker, JSStri
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
 static void MarkChildren(JSTracer *trc, JSScript *script);
+static void MarkChildren(JSTracer *trc, LazyScript *lazy);
 static void MarkChildren(JSTracer *trc, Shape *shape);
 static void MarkChildren(JSTracer *trc, BaseShape *base);
 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
 static void MarkChildren(JSTracer *trc, ion::IonCode *code);
 
 } /* namespace gc */
 } /* namespace js */
 
@@ -355,16 +356,17 @@ DeclMarkerImpl(IonCode, ion::IonCode)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, ArrayBufferObject)
 DeclMarkerImpl(Object, DebugScopeObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
 DeclMarkerImpl(Object, ScopeObject)
 DeclMarkerImpl(Script, JSScript)
+DeclMarkerImpl(LazyScript, LazyScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
 DeclMarkerImpl(String, JSString)
 DeclMarkerImpl(String, JSFlatString)
 DeclMarkerImpl(String, JSLinearString)
 DeclMarkerImpl(String, PropertyName)
 DeclMarkerImpl(TypeObject, js::types::TypeObject)
 
@@ -385,16 +387,19 @@ gc::MarkKind(JSTracer *trc, void **thing
         MarkInternal(trc, reinterpret_cast<JSObject **>(thingp));
         break;
       case JSTRACE_STRING:
         MarkInternal(trc, reinterpret_cast<JSString **>(thingp));
         break;
       case JSTRACE_SCRIPT:
         MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
         break;
+      case JSTRACE_LAZY_SCRIPT:
+        MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
+        break;
       case JSTRACE_SHAPE:
         MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
         break;
       case JSTRACE_BASE_SHAPE:
         MarkInternal(trc, reinterpret_cast<BaseShape **>(thingp));
         break;
       case JSTRACE_TYPE_OBJECT:
         MarkInternal(trc, reinterpret_cast<types::TypeObject **>(thingp));
@@ -788,16 +793,30 @@ PushMarkStack(GCMarker *gcmarker, JSScri
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
+PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
+{
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
+    JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
+
+    /*
+     * We mark lazy scripts directly rather than pushing on the stack as they
+     * only refer to normal scripts and to strings, and cannot recurse.
+     */
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        MarkChildren(gcmarker, thing);
+}
+
+static void
 ScanShape(GCMarker *gcmarker, Shape *shape);
 
 static void
 PushMarkStack(GCMarker *gcmarker, Shape *thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
     JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
 
@@ -993,16 +1012,22 @@ gc::MarkChildren(JSTracer *trc, JSString
 
 static void
 gc::MarkChildren(JSTracer *trc, JSScript *script)
 {
     script->markChildren(trc);
 }
 
 static void
+gc::MarkChildren(JSTracer *trc, LazyScript *lazy)
+{
+    lazy->markChildren(trc);
+}
+
+static void
 gc::MarkChildren(JSTracer *trc, Shape *shape)
 {
     shape->markChildren(trc);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, BaseShape *base)
 {
@@ -1148,16 +1173,20 @@ gc::PushArena(GCMarker *gcmarker, ArenaH
       case JSTRACE_STRING:
         PushArenaTyped<JSString>(gcmarker, aheader);
         break;
 
       case JSTRACE_SCRIPT:
         PushArenaTyped<JSScript>(gcmarker, aheader);
         break;
 
+      case JSTRACE_LAZY_SCRIPT:
+        PushArenaTyped<LazyScript>(gcmarker, aheader);
+        break;
+
       case JSTRACE_SHAPE:
         PushArenaTyped<js::Shape>(gcmarker, aheader);
         break;
 
       case JSTRACE_BASE_SHAPE:
         PushArenaTyped<js::BaseShape>(gcmarker, aheader);
         break;
 
@@ -1496,16 +1525,20 @@ js::TraceChildren(JSTracer *trc, void *t
       case JSTRACE_STRING:
         MarkChildren(trc, static_cast<JSString *>(thing));
         break;
 
       case JSTRACE_SCRIPT:
         MarkChildren(trc, static_cast<JSScript *>(thing));
         break;
 
+      case JSTRACE_LAZY_SCRIPT:
+        MarkChildren(trc, static_cast<LazyScript *>(thing));
+        break;
+
       case JSTRACE_SHAPE:
         MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
       case JSTRACE_IONCODE:
         MarkChildren(trc, (js::ion::IonCode *)thing);
         break;
 
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -96,16 +96,17 @@ DeclMarker(IonCode, ion::IonCode)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, ArrayBufferObject)
 DeclMarker(Object, DebugScopeObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
 DeclMarker(Object, ScopeObject)
 DeclMarker(Script, JSScript)
+DeclMarker(LazyScript, LazyScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
 DeclMarker(String, JSFlatString)
 DeclMarker(String, JSLinearString)
 DeclMarker(String, PropertyName)
 DeclMarker(TypeObject, types::TypeObject)
 
@@ -391,16 +392,22 @@ TraceKind(JSObject *obj)
 }
 
 inline JSGCTraceKind
 TraceKind(JSScript *script)
 {
     return JSTRACE_SCRIPT;
 }
 
+inline JSGCTraceKind
+TraceKind(LazyScript *lazy)
+{
+    return JSTRACE_LAZY_SCRIPT;
+}
+
 } /* namespace gc */
 
 void
 TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 } /* namespace js */
 
 #endif /* gc_marking_h___ */
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1545,17 +1545,17 @@ CodeGenerator::visitCallGeneric(LCallGen
     masm.cmpPtr(nargsreg, ImmWord(&js::FunctionClass));
     if (!bailoutIf(Assembler::NotEqual, call->snapshot()))
         return false;
 
     // Guard that calleereg is an interpreted function with a JSScript:
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
 
     // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
+    masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
     masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
@@ -1680,17 +1680,17 @@ CodeGenerator::visitCallKnown(LCallKnown
             masm.bind(&notPrimitive);
         }
 
         dropArguments(call->numStackArgs() + 1);
         return true;
     }
 
     // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
+    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);
 
     // Nestle the StackPointer up to the argument vector.
@@ -1889,17 +1889,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
     if (!apply->hasSingleTarget()) {
         masm.branchIfFunctionHasNoScript(calleereg, &invoke);
     } else {
         // Native single targets are handled by LCallNative.
         JS_ASSERT(!apply->getSingleTarget()->isNative());
     }
 
     // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
+    masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
 
     // Load script jitcode.
     masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
 
     // Call with an Ion frame or a rectifier frame.
     {
         // Create the frame descriptor.
         unsigned pushed = masm.framePushed();
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -7960,16 +7960,19 @@ IonBuilder::jsop_object(JSObject *obj)
     current->push(ins);
 
     return true;
 }
 
 bool
 IonBuilder::jsop_lambda(JSFunction *fun)
 {
+    if (fun->isInterpreted() && !fun->getOrCreateScript(cx))
+        return false;
+
     JS_ASSERT(script()->analysis()->usesScopeChain());
     if (fun->isArrow())
         return abort("bound arrow function");
     if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
         return abort("asm.js module function");
 
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
--- a/js/src/ion/arm/Trampoline-arm.cpp
+++ b/js/src/ion/arm/Trampoline-arm.cpp
@@ -411,17 +411,17 @@ IonRuntime::generateArgumentsRectifier(J
 
     // Construct IonJSFrameLayout.
     masm.ma_push(r0); // actual arguments.
     masm.ma_push(r1); // calleeToken.
     masm.ma_push(r6); // frame descriptor.
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
-    masm.ma_ldr(DTRAddr(r1, DtrOffImm(offsetof(JSFunction, u.i.script_))), r3);
+    masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
     masm.loadBaselineOrIonRaw(r3, r3, mode, NULL);
     masm.ma_callIonHalfPush(r3);
 
     uint32_t returnOffset = masm.currentOffset();
 
     // arg1
     //  ...
     // argN
--- a/js/src/ion/x64/Trampoline-x64.cpp
+++ b/js/src/ion/x64/Trampoline-x64.cpp
@@ -383,17 +383,17 @@ IonRuntime::generateArgumentsRectifier(J
 
     // Construct IonJSFrameLayout.
     masm.push(rdx); // numActualArgs
     masm.push(rax); // calleeToken
     masm.push(r9); // descriptor
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
-    masm.movq(Operand(rax, offsetof(JSFunction, u.i.script_)), rax);
+    masm.movq(Operand(rax, JSFunction::offsetOfNativeOrScript()), rax);
     masm.loadBaselineOrIonRaw(rax, rax, mode, NULL);
     masm.call(rax);
     uint32_t returnOffset = masm.currentOffset();
 
     // Remove the rectifier frame.
     masm.pop(r9);             // r9 <- descriptor with FrameType.
     masm.shrq(Imm32(FRAMESIZE_SHIFT), r9);
     masm.pop(r11);            // Discard calleeToken.
--- a/js/src/ion/x86/Trampoline-x86.cpp
+++ b/js/src/ion/x86/Trampoline-x86.cpp
@@ -374,17 +374,17 @@ IonRuntime::generateArgumentsRectifier(J
 
     // Construct IonJSFrameLayout.
     masm.push(edx); // number of actual arguments
     masm.push(eax); // calleeToken
     masm.push(ebx); // descriptor
 
     // Call the target function.
     // Note that this assumes the function is JITted.
-    masm.movl(Operand(eax, offsetof(JSFunction, u.i.script_)), eax);
+    masm.movl(Operand(eax, JSFunction::offsetOfNativeOrScript()), eax);
     masm.loadBaselineOrIonRaw(eax, eax, mode, NULL);
     masm.call(eax);
     uint32_t returnOffset = masm.currentOffset();
 
     // Remove the rectifier frame.
     masm.pop(ebx);            // ebx <- descriptor with FrameType.
     masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
     masm.pop(edi);            // Discard calleeToken.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2592,16 +2592,20 @@ JS_GetTraceThingInfo(char *buf, size_t b
                ? "substring"
                : "string";
         break;
 
       case JSTRACE_SCRIPT:
         name = "script";
         break;
 
+      case JSTRACE_LAZY_SCRIPT:
+        name = "lazyscript";
+        break;
+
       case JSTRACE_IONCODE:
         name = "ioncode";
         break;
 
       case JSTRACE_SHAPE:
         name = "shape";
         break;
 
@@ -2663,16 +2667,17 @@ JS_GetTraceThingInfo(char *buf, size_t b
 
           case JSTRACE_SCRIPT:
           {
             JSScript *script = static_cast<JSScript *>(thing);
             JS_snprintf(buf, bufsize, " %s:%u", script->filename(), unsigned(script->lineno));
             break;
           }
 
+          case JSTRACE_LAZY_SCRIPT:
           case JSTRACE_IONCODE:
           case JSTRACE_SHAPE:
           case JSTRACE_BASE_SHAPE:
           case JSTRACE_TYPE_OBJECT:
             break;
         }
     }
     buf[bufsize - 1] = '\0';
@@ -4914,16 +4919,21 @@ JS_CloneFunctionObject(JSContext *cx, JS
         return NULL;
     }
 
     /*
      * If a function was compiled to be lexically nested inside some other
      * script, we cannot clone it without breaking the compiler's assumptions.
      */
     RootedFunction fun(cx, funobj->toFunction());
+    if (fun->isInterpretedLazy()) {
+        AutoCompartment ac(cx, funobj);
+        if (!fun->getOrCreateScript(cx))
+            return NULL;
+    }
     if (fun->isInterpreted() && (fun->nonLazyScript()->enclosingStaticScope() ||
         (fun->nonLazyScript()->compileAndGo && !parent->isGlobal())))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
         return NULL;
     }
 
     if (fun->isBoundFunction()) {
@@ -5277,20 +5287,22 @@ AutoFile::open(JSContext *cx, const char
 JS::CompileOptions::CompileOptions(JSContext *cx)
     : principals(NULL),
       originPrincipals(NULL),
       version(cx->findVersion()),
       versionSet(false),
       utf8(false),
       filename(NULL),
       lineno(1),
+      column(0),
       compileAndGo(cx->hasOption(JSOPTION_COMPILE_N_GO)),
       forEval(false),
       noScriptRval(cx->hasOption(JSOPTION_NO_SCRIPT_RVAL)),
       selfHostingMode(false),
+      canLazilyParse(true),
       sourcePolicy(SAVE_SOURCE)
 {
 }
 
 JSScript *
 JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options,
             const jschar *chars, size_t length)
 {
@@ -5419,17 +5431,17 @@ JS_BufferIsCompilableUnit(JSContext *cx,
      * collect more buffered source.
      */
     result = JS_TRUE;
     exnState = JS_SaveExceptionState(cx);
     {
         CompileOptions options(cx);
         options.setCompileAndGo(false);
         Parser<frontend::FullParseHandler> parser(cx, options, chars, length,
-                                                  /* foldConstants = */ true);
+                                                  /* foldConstants = */ true, NULL, NULL);
         if (parser.init()) {
             older = JS_SetErrorReporter(cx, NULL);
             if (!parser.parse(obj) &&
                 parser.tokenStream.isUnexpectedEOF()) {
                 /*
                  * We ran into an error. If it was because we ran out of
                  * source, we return false so our caller knows to try to
                  * collect more buffered source.
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3889,38 +3889,42 @@ namespace JS {
 struct JS_PUBLIC_API(CompileOptions) {
     JSPrincipals *principals;
     JSPrincipals *originPrincipals;
     JSVersion version;
     bool versionSet;
     bool utf8;
     const char *filename;
     unsigned lineno;
+    unsigned column;
     bool compileAndGo;
     bool forEval;
     bool noScriptRval;
     bool selfHostingMode;
+    bool canLazilyParse;
     enum SourcePolicy {
         NO_SOURCE,
         LAZY_SOURCE,
         SAVE_SOURCE
     } sourcePolicy;
 
     explicit CompileOptions(JSContext *cx);
     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; }
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
+    CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
 };
 
 extern JS_PUBLIC_API(JSScript *)
 Compile(JSContext *cx, JSHandleObject obj, CompileOptions options,
         const char *bytes, size_t length);
 
 extern JS_PUBLIC_API(JSScript *)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -916,16 +916,18 @@ JS::IncrementalReferenceBarrier(void *pt
     AutoMarkInDeadZone amn(zone);
 
     if (kind == JSTRACE_OBJECT)
         JSObject::writeBarrierPre(static_cast<JSObject*>(cell));
     else if (kind == JSTRACE_STRING)
         JSString::writeBarrierPre(static_cast<JSString*>(cell));
     else if (kind == JSTRACE_SCRIPT)
         JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
+    else if (kind == JSTRACE_LAZY_SCRIPT)
+        LazyScript::writeBarrierPre(static_cast<LazyScript*>(cell));
     else if (kind == JSTRACE_SHAPE)
         Shape::writeBarrierPre(static_cast<Shape*>(cell));
     else if (kind == JSTRACE_BASE_SHAPE)
         BaseShape::writeBarrierPre(static_cast<BaseShape*>(cell));
     else if (kind == JSTRACE_TYPE_OBJECT)
         types::TypeObject::writeBarrierPre((types::TypeObject *) ptr);
     else
         JS_NOT_REACHED("invalid trace kind");
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -486,18 +486,23 @@ JSFunction::trace(JSTracer *trc)
         MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
                        toExtended()->extendedSlots, "nativeReserved");
     }
 
     if (atom_)
         MarkString(trc, &atom_, "atom");
 
     if (isInterpreted()) {
-        if (hasScript())
-            MarkScriptUnbarriered(trc, &u.i.script_, "script");
+        // Functions can be be marked as interpreted despite having no script
+        // yet at some points when parsing, and can be lazy with no lazy script
+        // for self hosted code.
+        if (hasScript() && u.i.s.script_)
+            MarkScriptUnbarriered(trc, &u.i.s.script_, "script");
+        else if (isInterpretedLazy() && u.i.s.lazy_)
+            MarkLazyScriptUnbarriered(trc, &u.i.s.lazy_, "lazyScript");
         if (u.i.env_)
             MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
     }
 }
 
 static void
 fun_trace(JSTracer *trc, JSObject *obj)
 {
@@ -579,28 +584,31 @@ FindBody(JSContext *cx, HandleFunction f
     *bodyEnd = end - chars;
     JS_ASSERT(*bodyStart <= *bodyEnd);
     return true;
 }
 
 JSString *
 js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
 {
-    StringBuffer out(cx);
-    RootedScript script(cx);
+    if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
+        return NULL;
 
     // If the object is an automatically-bound arrow function, get the source
     // of the pre-binding target.
     if (fun->isArrow() && fun->isBoundFunction()) {
         JSObject *target = fun->getBoundFunctionTarget();
         RootedFunction targetFun(cx, target->toFunction());
         JS_ASSERT(targetFun->isArrow());
         return FunctionToString(cx, targetFun, bodyOnly, lambdaParen);
     }
 
+    StringBuffer out(cx);
+    RootedScript script(cx);
+
     if (fun->hasScript()) {
         script = fun->nonLazyScript();
         if (script->isGeneratorExp) {
             if ((!bodyOnly && !out.append("function genexp() {")) ||
                 !out.append("\n    [generator expression]\n") ||
                 (!bodyOnly && !out.append("}")))
             {
                 return NULL;
@@ -1056,27 +1064,75 @@ inline size_t
 JSFunction::getBoundFunctionArgumentCount() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
 }
 
-bool
-JSFunction::initializeLazyScript(JSContext *cx)
+/* static */ bool
+JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFunction fun)
 {
-    JS_ASSERT(isInterpretedLazy());
-    const JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(getExtendedSlot(0).toPrivate());
-    Rooted<JSFunction*> self(cx, this);
+    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);
+
+        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;
+
+        /*
+         * 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);
+
+        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;
+        }
+
+        lazy->initScript(fun->nonLazyScript());
+        return true;
+    }
+
+    /* Lazily cloned self hosted script. */
+    JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(fun->getExtendedSlot(0).toPrivate());
     RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
     if (!funAtom)
         return false;
     Rooted<PropertyName *> funName(cx, funAtom->asPropertyName());
-    return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, self);
+    return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, fun);
 }
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
 JSBool
 js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
 {
     RootedFunction fun(cx, vp[0].toObject().toFunction());
     JS_ASSERT(fun->isBoundFunction());
@@ -1499,35 +1555,36 @@ js::CloneFunctionObject(JSContext *cx, H
                         NewObjectKind newKindArg /* = GenericObject */)
 {
     JS_ASSERT(parent);
     JS_ASSERT(!fun->isBoundFunction());
 
     bool useSameScript = cx->compartment == fun->compartment() &&
                          !fun->hasSingletonType() &&
                          !types::UseNewTypeForClone(fun);
+
+    if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
+        return NULL;
+
     NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
     JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent),
                                                  allocKind, newKind);
     if (!cloneobj)
         return NULL;
     RootedFunction clone(cx, cloneobj->toFunction());
 
     clone->nargs = fun->nargs;
     clone->flags = fun->flags & ~JSFunction::EXTENDED;
-    if (fun->isInterpreted()) {
-        if (fun->isInterpretedLazy()) {
-            RootedFunction cloneRoot(cx, clone);
-            AutoCompartment ac(cx, fun);
-            if (!fun->getOrCreateScript(cx))
-                return NULL;
-            clone = cloneRoot;
-        }
+    if (fun->hasScript()) {
         clone->initScript(fun->nonLazyScript());
         clone->initEnvironment(parent);
+    } else if (fun->isInterpretedLazy()) {
+        LazyScript *lazy = fun->lazyScriptOrNull();
+        clone->initLazyScript(lazy);
+        clone->initEnvironment(parent);
     } else {
         clone->initNative(fun->native(), fun->jitInfo());
     }
     clone->initAtom(fun->displayAtom());
 
     if (allocKind == JSFunction::ExtendedFinalizeKind) {
         clone->flags |= JSFunction::EXTENDED;
         if (fun->isExtended() && fun->compartment() == cx->compartment) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -63,40 +63,48 @@ class JSFunction : public JSObject
         class Native {
             friend class JSFunction;
             js::Native          native;       /* native method pointer or null */
             const JSJitInfo     *jitinfo;     /* Information about this function to be
                                                  used by the JIT;
                                                  use the accessor! */
         } n;
         struct Scripted {
-            JSScript    *script_; /* interpreted bytecode descriptor or null;
-                                     use the accessor! */
+            union {
+                JSScript *script_; /* interpreted bytecode descriptor or null;
+                                      use the accessor! */
+                js::LazyScript *lazy_; /* lazily compiled script, or NULL */
+            } s;
             JSObject    *env_;    /* environment for new activations;
                                      use the accessor! */
         } i;
         void            *nativeOrScript;
     } u;
   private:
     js::HeapPtrAtom  atom_;       /* name for diagnostics and decompiling */
 
   public:
 
+    bool isHeavyweight() {
+        /* The heavyweight flag is not set until the script is parsed. */
+        JS_ASSERT(!isInterpretedLazy());
+        return flags & HEAVYWEIGHT;
+    }
+
     /* A function can be classified as either native (C++) or interpreted (JS): */
     bool isInterpreted()            const { return flags & (INTERPRETED | INTERPRETED_LAZY); }
     bool isNative()                 const { return !isInterpreted(); }
 
     /* Possible attributes of a native function: */
     bool isNativeConstructor()      const { return flags & NATIVE_CTOR; }
 
     /* Possible attributes of an interpreted function: */
-    bool isHeavyweight()            const { return flags & HEAVYWEIGHT; }
     bool isFunctionPrototype()      const { return flags & IS_FUN_PROTO; }
     bool isInterpretedLazy()        const { return flags & INTERPRETED_LAZY; }
-    bool hasScript()                const { return isInterpreted() && u.i.script_; }
+    bool hasScript()                const { return flags & INTERPRETED; }
     bool isExprClosure()            const { return flags & EXPR_CLOSURE; }
     bool hasGuessedAtom()           const { return flags & HAS_GUESSED_ATOM; }
     bool isLambda()                 const { return flags & LAMBDA; }
     bool isSelfHostedBuiltin()      const { return flags & SELF_HOSTED; }
     bool isSelfHostedConstructor()  const { return flags & SELF_HOSTED_CTOR; }
     bool hasRest()                  const { return flags & HAS_REST; }
     bool hasDefaults()              const { return flags & HAS_DEFAULTS; }
     bool isWrappable()              const {
@@ -201,75 +209,91 @@ class JSFunction : public JSObject
      */
     inline JSObject *environment() const;
     inline void setEnvironment(JSObject *obj);
     inline void initEnvironment(JSObject *obj);
 
     static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
     static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
 
-    bool initializeLazyScript(JSContext *cx);
+    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 (!self->initializeLazyScript(cx))
+            if (!createScriptForLazilyInterpretedFunction(cx, self))
                 return NULL;
-            return self->u.i.script_;
+            JS_ASSERT(self->hasScript());
+            return self->u.i.s.script_;
         }
         JS_ASSERT(hasScript());
-        return u.i.script_;
+        return u.i.s.script_;
     }
 
     static bool maybeGetOrCreateScript(JSContext *cx, js::HandleFunction fun,
                                        js::MutableHandle<JSScript*> script)
     {
         if (fun->isNative()) {
             script.set(NULL);
             return true;
         }
         script.set(fun->getOrCreateScript(cx));
         return fun->hasScript();
     }
 
     JSScript *nonLazyScript() const {
         JS_ASSERT(hasScript());
-        return JS::HandleScript::fromMarkedLocation(&u.i.script_);
+        return JS::HandleScript::fromMarkedLocation(&u.i.s.script_);
     }
 
     JSScript *maybeNonLazyScript() const {
-        return isInterpreted() ? nonLazyScript() : NULL;
+        return hasScript() ? nonLazyScript() : NULL;
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
-        return *(js::HeapPtrScript *)&u.i.script_;
+        return *(js::HeapPtrScript *)&u.i.s.script_;
+    }
+
+    // A lazily interpreted function will have an associated LazyScript if the
+    // script has not yet been parsed. For functions whose scripts are lazily
+    // cloned from self hosted code, there is no LazyScript.
+
+    js::LazyScript *lazyScript() const {
+        JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
+        return u.i.s.lazy_;
+    }
+
+    js::LazyScript *lazyScriptOrNull() const {
+        JS_ASSERT(isInterpretedLazy());
+        return u.i.s.lazy_;
     }
 
     inline void setScript(JSScript *script_);
     inline void initScript(JSScript *script_);
+    inline void initLazyScript(js::LazyScript *script);
 
     JSNative native() const {
         JS_ASSERT(isNative());
         return u.n.native;
     }
 
     JSNative maybeNative() const {
         return isInterpreted() ? NULL : native();
     }
 
     inline void initNative(js::Native native, const JSJitInfo *jitinfo);
     inline const JSJitInfo *jitInfo() const;
     inline void setJitInfo(const JSJitInfo *data);
 
     static unsigned offsetOfNativeOrScript() {
-        JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_));
+        JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.s.script_));
         JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
         return offsetof(JSFunction, u.nativeOrScript);
     }
 
 #if JS_BITS_PER_WORD == 32
     static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND;
     static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND;
 #else
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -163,42 +163,57 @@ SkipScopeParent(JSObject *parent)
 {
     if (!parent)
         return NULL;
     while (parent->isScope())
         parent = &parent->asScope().enclosingScope();
     return parent;
 }
 
+inline bool
+CanReuseFunctionForClone(JSContext *cx, HandleFunction fun)
+{
+    if (!fun->hasSingletonType())
+        return false;
+    if (fun->isInterpretedLazy()) {
+        LazyScript *lazy = fun->lazyScript();
+        if (lazy->hasBeenCloned())
+            return false;
+        lazy->setHasBeenCloned();
+    } else {
+        JSScript *script = fun->nonLazyScript();
+        if (script->hasBeenCloned)
+            return false;
+        script->hasBeenCloned = true;
+    }
+    return true;
+}
+
 inline JSFunction *
 CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent,
                                   NewObjectKind newKind = GenericObject)
 {
     /*
      * For attempts to clone functions at a function definition opcode,
-     * don't perform the clone if the function has singleton type. This
+     * try to avoid the the clone if the function has singleton type. This
      * was called pessimistically, and we need to preserve the type's
      * property that if it is singleton there is only a single object
      * with its type in existence.
      *
      * For functions inner to run once lambda, it may be possible that
      * the lambda runs multiple times and we repeatedly clone it. In these
      * cases, fall through to CloneFunctionObject, which will deep clone
      * the function's script.
      */
-    if (fun->hasSingletonType()) {
-        RootedScript script(cx, fun->getOrCreateScript(cx));
-        if (!script->hasBeenCloned) {
-            script->hasBeenCloned = true;
-            Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
-            if (!JSObject::setParent(cx, fun, obj))
-                return NULL;
-            fun->setEnvironment(parent);
-            return fun;
-        }
+    if (CanReuseFunctionForClone(cx, fun)) {
+        RootedObject obj(cx, SkipScopeParent(parent));
+        if (!JSObject::setParent(cx, fun, obj))
+            return NULL;
+        fun->setEnvironment(parent);
+        return fun;
     }
 
     // These intermediate variables are needed to avoid link errors on some
     // platforms.  Sigh.
     gc::AllocKind finalizeKind = JSFunction::FinalizeKind;
     gc::AllocKind extendedFinalizeKind = JSFunction::ExtendedFinalizeKind;
     gc::AllocKind kind = fun->isExtended()
                          ? extendedFinalizeKind
@@ -217,16 +232,27 @@ JSFunction::setScript(JSScript *script_)
 
 inline void
 JSFunction::initScript(JSScript *script_)
 {
     JS_ASSERT(isInterpreted());
     mutableScript().init(script_);
 }
 
+inline void
+JSFunction::initLazyScript(js::LazyScript *lazy)
+{
+    JS_ASSERT(isInterpreted());
+
+    flags &= ~INTERPRETED;
+    flags |= INTERPRETED_LAZY;
+
+    u.i.s.lazy_ = lazy;
+}
+
 inline JSObject *
 JSFunction::getBoundFunctionTarget() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     /* Bound functions abuse |parent| to store their target function. */
     return getParent();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -116,16 +116,17 @@ const uint32_t Arena::ThingSizes[] = {
     sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     sizeof(JSScript),           /* FINALIZE_SCRIPT              */
+    sizeof(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
     sizeof(Shape),              /* FINALIZE_SHAPE               */
     sizeof(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     sizeof(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
     sizeof(JSShortString),      /* FINALIZE_SHORT_STRING        */
     sizeof(JSString),           /* FINALIZE_STRING              */
     sizeof(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     sizeof(ion::IonCode),       /* FINALIZE_IONCODE             */
 };
@@ -141,16 +142,17 @@ const uint32_t Arena::FirstThingOffsets[
     OFFSET(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
     OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
     OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     OFFSET(JSScript),           /* FINALIZE_SCRIPT              */
+    OFFSET(LazyScript),         /* FINALIZE_LAZY_SCRIPT         */
     OFFSET(Shape),              /* FINALIZE_SHAPE               */
     OFFSET(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     OFFSET(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
     OFFSET(JSShortString),      /* FINALIZE_SHORT_STRING        */
     OFFSET(JSString),           /* FINALIZE_STRING              */
     OFFSET(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
     OFFSET(ion::IonCode),       /* FINALIZE_IONCODE             */
 };
@@ -206,16 +208,17 @@ 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,
@@ -422,16 +425,18 @@ FinalizeArenas(FreeOp *fop,
       case FINALIZE_OBJECT8_BACKGROUND:
       case FINALIZE_OBJECT12:
       case FINALIZE_OBJECT12_BACKGROUND:
       case FINALIZE_OBJECT16:
       case FINALIZE_OBJECT16_BACKGROUND:
         return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget);
       case FINALIZE_SCRIPT:
         return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget);
+      case FINALIZE_LAZY_SCRIPT:
+        return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget);
       case FINALIZE_SHAPE:
         return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget);
       case FINALIZE_BASE_SHAPE:
         return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget);
       case FINALIZE_TYPE_OBJECT:
         return FinalizeTypedArenas<types::TypeObject>(fop, src, dest, thingKind, budget);
       case FINALIZE_STRING:
         return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget);
@@ -1430,16 +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);
 }
 
 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
@@ -114,16 +114,17 @@ MapAllocToTraceKind(AllocKind kind)
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16_BACKGROUND */
         JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
+        JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
         JSTRACE_SHAPE,      /* FINALIZE_SHAPE */
         JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
         JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
         JSTRACE_IONCODE,    /* FINALIZE_IONCODE */
     };
@@ -135,16 +136,17 @@ template <typename T> struct MapTypeToTr
 template <> struct MapTypeToTraceKind<JSObject>         { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSFunction>       { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArgumentsObject>  { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ArrayBufferObject>{ const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<DebugScopeObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<GlobalObject>     { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<ScopeObject>      { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSScript>         { const static JSGCTraceKind kind = JSTRACE_SCRIPT; };
+template <> struct MapTypeToTraceKind<LazyScript>       { const static JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
 template <> struct MapTypeToTraceKind<Shape>            { const static JSGCTraceKind kind = JSTRACE_SHAPE; };
 template <> struct MapTypeToTraceKind<BaseShape>        { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<UnownedBaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
 template <> struct MapTypeToTraceKind<types::TypeObject>{ const static JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
 template <> struct MapTypeToTraceKind<JSAtom>           { const static JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSString>         { const static JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSFlatString>     { const static JSGCTraceKind kind = JSTRACE_STRING; };
 template <> struct MapTypeToTraceKind<JSLinearString>   { const static JSGCTraceKind kind = JSTRACE_STRING; };
@@ -165,16 +167,17 @@ IsNurseryAllocable(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 */
+        false,     /* FINALIZE_LAZY_SCRIPT */
         false,     /* FINALIZE_SHAPE */
         false,     /* FINALIZE_BASE_SHAPE */
         false,     /* FINALIZE_TYPE_OBJECT */
         false,     /* FINALIZE_SHORT_STRING */
         false,     /* FINALIZE_STRING */
         false,     /* FINALIZE_EXTERNAL_STRING */
         false,     /* FINALIZE_IONCODE */
     };
@@ -196,16 +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 */
         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/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -586,16 +586,23 @@ js_NewGCExternalString(JSContext *cx)
 
 inline JSScript *
 js_NewGCScript(JSContext *cx)
 {
     return js::gc::NewGCThing<JSScript, js::CanGC>(cx, js::gc::FINALIZE_SCRIPT,
                                                    sizeof(JSScript), js::gc::TenuredHeap);
 }
 
+inline js::LazyScript *
+js_NewGCLazyScript(JSContext *cx)
+{
+    return js::gc::NewGCThing<js::LazyScript, js::CanGC>(cx, js::gc::FINALIZE_LAZY_SCRIPT,
+                                                         sizeof(js::LazyScript), js::gc::TenuredHeap);
+}
+
 inline js::Shape *
 js_NewGCShape(JSContext *cx)
 {
     return js::gc::NewGCThing<js::Shape, js::CanGC>(cx, js::gc::FINALIZE_SHAPE,
                                                     sizeof(js::Shape), js::gc::TenuredHeap);
 }
 
 template <js::AllowGC allowGC>
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5944,30 +5944,22 @@ JSScript::makeAnalysis(JSContext *cx)
     }
 
     return true;
 }
 
 /* static */ bool
 JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */)
 {
-    JS_ASSERT(fun->nonLazyScript());
-    JS_ASSERT(fun->nonLazyScript()->function() == fun);
-
     if (!cx->typeInferenceEnabled())
         return true;
 
     if (singleton) {
         if (!setSingletonType(cx, fun))
             return false;
-    } else if (UseNewTypeForClone(fun)) {
-        /*
-         * Leave the default unknown-properties type for the function, it
-         * should not be used by scripts or appear in type sets.
-         */
     } else {
         RootedObject funProto(cx, fun->getProto());
         TypeObject *type = cx->compartment->types.newTypeObject(cx, &FunctionClass, funProto);
         if (!type)
             return false;
 
         fun->setType(type);
         type->interpretedFunction = fun;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -711,17 +711,17 @@ UseNewTypeAtEntry(JSContext *cx, StackFr
 }
 
 inline bool
 UseNewTypeForClone(JSFunction *fun)
 {
     if (!fun->isInterpreted())
         return false;
 
-    if (fun->nonLazyScript()->shouldCloneAtCallsite)
+    if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite)
         return true;
 
     if (fun->isArrow())
         return true;
 
     if (fun->hasSingletonType())
         return false;
 
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -52,16 +52,17 @@ struct IteratorClosure
 
 size_t
 ZoneStats::GCHeapThingsSize()
 {
     // These are just the GC-thing measurements.
     size_t n = 0;
     n += gcHeapStringsNormal;
     n += gcHeapStringsShort;
+    n += gcHeapLazyScripts;
     n += gcHeapTypeObjects;
     n += gcHeapIonCodes;
 
     return n;
 }
 
 size_t
 CompartmentStats::GCHeapThingsSize()
@@ -266,16 +267,23 @@ StatsCellCallback(JSRuntime *rt, void *d
         SourceSet::AddPtr entry = closure->seenSources.lookupForAdd(ss);
         if (!entry) {
             closure->seenSources.add(entry, ss); // Not much to be done on failure.
             rtStats->runtime.scriptSources += ss->sizeOfIncludingThis(rtStats->mallocSizeOf_);
         }
         break;
       }
 
+      case JSTRACE_LAZY_SCRIPT: {
+        LazyScript *lazy = static_cast<LazyScript *>(thing);
+        zStats->gcHeapLazyScripts += thingSize;
+        zStats->lazyScripts += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        break;
+      }
+
       case JSTRACE_IONCODE: {
 #ifdef JS_ION
         zStats->gcHeapIonCodes += thingSize;
         // The code for a script is counted in ExecutableAllocator::sizeOfCode().
 #endif
         break;
       }
 
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -483,16 +483,22 @@ IsArgOp(JSOp op)
 
 inline bool
 IsLocalOp(JSOp op)
 {
     return JOF_OPTYPE(op) == JOF_LOCAL;
 }
 
 inline bool
+IsAliasedVarOp(JSOp op)
+{
+    return JOF_OPTYPE(op) == JOF_SCOPECOORD;
+}
+
+inline bool
 IsGlobalOp(JSOp op)
 {
     return js_CodeSpec[op].format & JOF_GNAME;
 }
 
 inline bool
 IsEqualityOp(JSOp op)
 {
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -78,16 +78,17 @@ struct Class;
 
 class RegExpGuard;
 class RegExpObject;
 class RegExpObjectBuilder;
 class RegExpShared;
 class RegExpStatics;
 class MatchPairs;
 class PropertyName;
+class LazyScript;
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -145,19 +145,20 @@ typedef enum JSIterateOp {
 
 /* See JSVAL_TRACE_KIND and JSTraceCallback in jsapi.h. */
 typedef enum {
     JSTRACE_OBJECT,
     JSTRACE_STRING,
     JSTRACE_SCRIPT,
 
     /*
-     * Trace kinds internal to the engine. The embedding can only them if it
-     * implements JSTraceCallback.
+     * Trace kinds internal to the engine. The embedding can only see them if
+     * it implements JSTraceCallback.
      */
+    JSTRACE_LAZY_SCRIPT,
     JSTRACE_IONCODE,
     JSTRACE_SHAPE,
     JSTRACE_BASE_SHAPE,
     JSTRACE_TYPE_OBJECT,
     JSTRACE_LAST = JSTRACE_TYPE_OBJECT
 } JSGCTraceKind;
 
 /* Struct typedefs and class forward declarations. */
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3064,17 +3064,19 @@ reflect_parse(JSContext *cx, uint32_t ar
     JSStableString *stable = src->ensureStable(cx);
     if (!stable)
         return JS_FALSE;
 
     const StableCharPtr chars = stable->chars();
     size_t length = stable->length();
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
-    Parser<FullParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
+    options.setCanLazilyParse(false);
+    Parser<FullParseHandler> parser(cx, options, chars.get(), length,
+                                    /* foldConstants = */ false, NULL, NULL);
     if (!parser.init())
         return JS_FALSE;
 
     serialize.setParser(&parser);
 
     ParseNode *pn = parser.parse(NULL);
     if (!pn)
         return JS_FALSE;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1208,27 +1208,25 @@ SourceDataCache::put(ScriptSource *ss, J
 
 void
 SourceDataCache::purge()
 {
     js_delete(map_);
     map_ = NULL;
 }
 
-JSStableString *
-ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
+const jschar *
+ScriptSource::chars(JSContext *cx)
 {
-    const jschar *chars;
 #ifdef USE_ZLIB
     Rooted<JSStableString *> cached(cx, NULL);
 #endif
 #ifdef JS_THREADSAFE
-    if (!ready()) {
-        chars = cx->runtime->sourceCompressorThread.currentChars();
-    } else
+    if (!ready())
+        return cx->runtime->sourceCompressorThread.currentChars();
 #endif
 #ifdef USE_ZLIB
     if (compressed()) {
         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)
@@ -1242,24 +1240,31 @@ ScriptSource::substring(JSContext *cx, u
             decompressed[length_] = 0;
             cached = js_NewString<CanGC>(cx, decompressed, length_);
             if (!cached) {
                 js_free(decompressed);
                 return NULL;
             }
             cx->runtime->sourceDataCache.put(this, cached);
         }
-        chars = cached->chars().get();
-        JS_ASSERT(chars);
-    } else {
-        chars = data.source;
+        return cached->chars().get();
     }
+    return data.source;
 #else
-    chars = data.source;
+    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)
+        return NULL;
     JSFlatString *flatStr = js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
     if (!flatStr)
         return NULL;
     return flatStr->ensureStable(cx);
 }
 
 bool
 ScriptSource::setSourceCopy(JSContext *cx, const jschar *src, uint32_t length,
@@ -1662,16 +1667,18 @@ ScriptDataSize(uint32_t nbindings, uint3
     return size;
 }
 
 JSScript *
 JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun,
                  const CompileOptions &options, unsigned staticLevel,
                  JS::HandleScriptSource sourceObject, uint32_t bufStart, uint32_t bufEnd)
 {
+    JS_ASSERT(bufStart <= bufEnd);
+
     RootedScript script(cx, js_NewGCScript(cx));
     if (!script)
         return NULL;
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
     script->enclosingScopeOrOriginalFunction_ = enclosingScope;
@@ -1953,17 +1960,17 @@ JSScript::enclosingScriptsCompiledSucces
      * 'script' field will be NULL until the enclosing script successfully
      * compiles. Thus, we can detect failed compilation by looking for
      * JSFunctions in the enclosingScope chain without scripts.
      */
     JSObject *enclosing = enclosingStaticScope();
     while (enclosing) {
         if (enclosing->isFunction()) {
             JSFunction *fun = enclosing->toFunction();
-            if (!fun->hasScript())
+            if (!fun->hasScript() || !fun->nonLazyScript())
                 return false;
             enclosing = fun->nonLazyScript()->enclosingStaticScope();
         } else {
             enclosing = enclosing->asStaticBlock().enclosingStaticScope();
         }
     }
     return true;
 }
@@ -2289,16 +2296,18 @@ js::CloneScript(JSContext *cx, HandleObj
                 if (StaticBlockObject *enclosingBlock = innerBlock->enclosingBlock())
                     enclosingScope = objects[FindBlockIndex(src, *enclosingBlock)];
                 else
                     enclosingScope = fun;
 
                 clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
             } else if (obj->isFunction()) {
                 RootedFunction innerFun(cx, obj->toFunction());
+                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;
 
@@ -2732,16 +2741,41 @@ JSScript::markChildren(JSTracer *trc)
     }
 
 #ifdef JS_ION
     ion::TraceIonScripts(trc, this);
 #endif
 }
 
 void
+LazyScript::markChildren(JSTracer *trc)
+{
+    if (parent_)
+        MarkScriptUnbarriered(trc, &parent_, "lazyScriptParent");
+
+    if (script_)
+        MarkScriptUnbarriered(trc, &script_, "lazyScript");
+
+    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_);
+}
+
+void
 JSScript::setArgumentsHasVarBinding()
 {
     argsHasVarBinding_ = true;
     needsArgsAnalysis_ = true;
 }
 
 void
 JSScript::setNeedsArgsObj(bool needsArgsObj)
@@ -2877,16 +2911,39 @@ JSScript::formalIsAliased(unsigned argSl
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
+/* static */ LazyScript *
+LazyScript::Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
+                   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;
+    if (bytes) {
+        table = cx->malloc_(bytes);
+        if (!table)
+            return NULL;
+    }
+
+    LazyScript *res = js_NewGCLazyScript(cx);
+    if (!res)
+        return NULL;
+
+    return new (res) LazyScript(table, numFreeVariables, numInnerFunctions, begin, end, lineno, column);
+}
+
 void
 JSScript::updateBaselineOrIonRaw()
 {
 #ifdef JS_ION
     if (hasIonScript()) {
         baselineOrIonRaw = ion->method()->raw();
         baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
     } else if (hasBaselineScript()) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -331,16 +331,17 @@ class JSScript : public js::gc::Cell
 
     uint32_t        lineno;     /* base line number of script */
 
     uint32_t        mainOffset; /* offset of main entry point from code, after
                                    predef'ing prolog */
 
     uint32_t        natoms;     /* length of atoms array */
 
+    /* Range of characters in scriptSource which contains this script's source. */
     uint32_t        sourceStart;
     uint32_t        sourceEnd;
 
   private:
     uint32_t        useCount;   /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
     uint32_t        PADDING32;
@@ -411,16 +412,19 @@ 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            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 */
     bool            uninlineable:1;   /* script is considered uninlineable by analysis */
 
+    /* Set for functions defined at the top level within an 'eval' script. */
+    bool directlyInsideEval: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
@@ -787,16 +791,21 @@ class JSScript : public js::gc::Cell
     }
 
     JSObject *getObject(size_t index) {
         js::ObjectArray *arr = objects();
         JS_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
+    size_t innerObjectsStart() {
+        // The first object contains the caller if savedCallerFun is used.
+        return savedCallerFun ? 1 : 0;
+    }
+
     JSObject *getObject(jsbytecode *pc) {
         JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
         return getObject(GET_UINT32_INDEX(pc));
     }
 
     JSVersion getVersion() const {
         return JSVersion(version);
     }
@@ -1036,16 +1045,17 @@ struct ScriptSource
     uint32_t length() const {
         JS_ASSERT(hasSourceData());
         return length_;
     }
     bool argumentsNotIncluded() const {
         JS_ASSERT(hasSourceData());
         return argumentsNotIncluded_;
     }
+    const jschar *chars(JSContext *cx);
     JSStableString *substring(JSContext *cx, uint32_t start, uint32_t stop);
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
 
     // XDR handling
     template <XDRMode mode>
     bool performXDR(XDRState<mode> *xdr);
 
     bool setFilename(JSContext *cx, const char *filename);
@@ -1098,16 +1108,171 @@ class ScriptSourceObject : public JSObje
             this->source()->decref();
         setReservedSlot(SOURCE_SLOT, PrivateValue(source));
     }
 
   private:
     static const uint32_t SOURCE_SLOT = 0;
 };
 
+// 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_;
+
+    // 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_;
+    uint32_t numInnerFunctions_ : 26;
+
+    bool strict_ : 1;
+    bool bindingsAccessedDynamically_ : 1;
+    bool hasDebuggerStatement_ : 1;
+    bool directlyInsideEval_:1;
+    bool 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);
+    }
+
+  public:
+    static LazyScript *Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
+                              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;
+    }
+    JSScript *maybeScript() {
+        return script_;
+    }
+
+    uint32_t numFreeVariables() const {
+        return numFreeVariables_;
+    }
+    HeapPtrAtom *freeVariables() {
+        return (HeapPtrAtom *)table_;
+    }
+
+    uint32_t numInnerFunctions() const {
+        return numInnerFunctions_;
+    }
+    HeapPtrFunction *innerFunctions() {
+        return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
+    }
+
+    bool strict() const {
+        return strict_;
+    }
+    void setStrict() {
+        strict_ = true;
+    }
+
+    bool bindingsAccessedDynamically() const {
+        return bindingsAccessedDynamically_;
+    }
+    void setBindingsAccessedDynamically() {
+        bindingsAccessedDynamically_ = true;
+    }
+
+    bool hasDebuggerStatement() const {
+        return hasDebuggerStatement_;
+    }
+    void setHasDebuggerStatement() {
+        hasDebuggerStatement_ = true;
+    }
+
+    bool directlyInsideEval() const {
+        return directlyInsideEval_;
+    }
+    void setDirectlyInsideEval() {
+        directlyInsideEval_ = true;
+    }
+
+    bool hasBeenCloned() const {
+        return hasBeenCloned_;
+    }
+    void setHasBeenCloned() {
+        hasBeenCloned_ = true;
+    }
+
+    ScriptSource *source() const {
+        return parent()->scriptSource();
+    }
+    uint32_t begin() const {
+        return begin_;
+    }
+    uint32_t end() const {
+        return end_;
+    }
+    uint32_t lineno() const {
+        return lineno_;
+    }
+    uint32_t column() const {
+        return column_;
+    }
+
+    Zone *zone() const {
+        return Cell::tenuredZone();
+    }
+
+    void markChildren(JSTracer *trc);
+    void finalize(js::FreeOp *fop);
+
+    size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
+    {
+        return mallocSizeOf(table_);
+    }
+
+    static inline void writeBarrierPre(LazyScript *lazy);
+};
+
 #ifdef JS_THREADSAFE
 /*
  * Background thread to compress JS source code. This happens only while parsing
  * and bytecode generation is happening in the main thread. If needed, the
  * compiler waits for compression to complete before returning.
  *
  * To use it, you have to have a SourceCompressionToken, tok, with tok.ss and
  * tok.chars set to the proper values. When the SourceCompressionToken is
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -179,16 +179,33 @@ JSScript::writeBarrierPre(JSScript *scri
 #endif
 }
 
 inline void
 JSScript::writeBarrierPost(JSScript *script, void *addr)
 {
 }
 
+/* static */ inline void
+js::LazyScript::writeBarrierPre(js::LazyScript *lazy)
+{
+#ifdef JSGC_INCREMENTAL
+    if (!lazy)
+        return;
+
+    JS::Zone *zone = lazy->zone();
+    if (zone->needsBarrier()) {
+        JS_ASSERT(!zone->rt->isHeapBusy());
+        js::LazyScript *tmp = lazy;
+        MarkLazyScriptUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == lazy);
+    }
+#endif
+}
+
 inline JSPrincipals *
 JSScript::principals()
 {
     return compartment()->principals;
 }
 
 inline JSFunction *
 JSScript::originalFunction() const {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3163,17 +3163,17 @@ Parse(JSContext *cx, unsigned argc, jsva
 
     JSString *scriptContents = args[0].toString();
     CompileOptions options(cx);
     options.setFileAndLine("<string>", 1)
            .setCompileAndGo(false);
     Parser<FullParseHandler> parser(cx, options,
                                     JS_GetStringCharsZ(cx, scriptContents),
                                     JS_GetStringLength(scriptContents),
-                                    /* foldConstants = */ true);
+                                    /* foldConstants = */ true, NULL, NULL);
     if (!parser.init())
         return false;
 
     ParseNode *pn = parser.parse(NULL);
     if (!pn)
         return false;
 #ifdef DEBUG
     DumpParseTree(pn);
@@ -3203,26 +3203,25 @@ SyntaxParse(JSContext *cx, unsigned argc
 
     JSString *scriptContents = args[0].toString();
     CompileOptions options(cx);
     options.setFileAndLine("<string>", 1)
            .setCompileAndGo(false);
 
     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
     size_t length = JS_GetStringLength(scriptContents);
-    Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length,
-                                                /* foldConstants = */ false);
+    Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length, false, NULL, NULL);
     if (!parser.init())
         return false;
 
     bool succeeded = parser.parse(NULL);
     if (cx->isExceptionPending())
         return false;
 
-    if (!succeeded && !parser.hadUnknownResult()) {
+    if (!succeeded && !parser.hadAbortedSyntaxParse()) {
         // If no exception is posted, either there was an OOM or a language
         // feature unhandled by the syntax parser was encountered.
         JS_ASSERT(cx->runtime->hadOutOfMemory);
         return false;
     }
 
     args.rval().setBoolean(succeeded);
     return true;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2887,39 +2887,54 @@ 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)
         return false;
     if (script->hasObjects()) {
         /*
          * script->savedCallerFun indicates that this is a direct eval script
          * and the calling function is stored as script->objects()->vector[0].
-         * It is not really a child script of this script, so skip it.
+         * It is not really a child script of this script, so skip it using
+         * innerObjectsStart().
          */
         ObjectArray *objects = script->objects();
         RootedFunction fun(cx);
         RootedScript funScript(cx);
         RootedObject obj(cx), s(cx);
-        for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
+        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);
@@ -4117,17 +4132,18 @@ js::EvaluateInEnv(JSContext *cx, Handle<
      * calls and properly compute a static level. In practice, any non-zero
      * static level will suffice.
      */
     CompileOptions options(cx);
     options.setPrincipals(env->compartment()->principals)
            .setCompileAndGo(true)
            .setForEval(true)
            .setNoScriptRval(false)
-           .setFileAndLine(filename, lineno);
+           .setFileAndLine(filename, lineno)
+           .setCanLazilyParse(false);
     RootedScript callerScript(cx, frame ? frame.script() : NULL);
     RootedScript script(cx, frontend::CompileScript(cx, env, callerScript,
                                                     options, chars.get(), length,
                                                     /* source = */ NULL,
                                                     /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
@@ -4450,16 +4466,19 @@ 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++) {
@@ -4491,16 +4510,19 @@ 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;
 }
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -610,22 +610,21 @@ js::ParallelDo::enqueueInitialScript(Exe
     if (!fun_->isFunction())
         return sequentialExecution(true, status);
 
     RootedFunction callee(cx_, fun_->toFunction());
 
     if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
         return sequentialExecution(true, status);
 
-    if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
-        return sequentialExecution(true, status);
-
     // If this function has not been run enough to enable parallel
     // execution, perform a warmup.
-    RootedScript script(cx_, callee->nonLazyScript());
+    RootedScript script(cx_, callee->getOrCreateScript(cx_));
+    if (!script)
+        return RedLight;
     if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
         if (warmupExecution(status) == RedLight)
             return RedLight;
     }
 
     // If the main script is already compiled, and we have no reason
     // to suspect any of its callees are not compiled, then we can
     // just skip the compilation step.
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -512,16 +512,17 @@ JSRuntime::initSelfHosting(JSContext *cx
         return false;
 
     if (!JS_DefineFunctions(cx, shg, intrinsic_functions))
         return false;
 
     CompileOptions options(cx);
     options.setFileAndLine("self-hosted", 1);
     options.setSelfHostingMode(true);
+    options.setCanLazilyParse(false);
     options.setSourcePolicy(CompileOptions::NO_SOURCE);
     options.setVersion(JSVERSION_LATEST);
 
     /*
      * Set a temporary error reporter printing to stderr because it is too
      * early in the startup process for any other reporter to be registered
      * and we don't want errors in self-hosted code to be silently swallowed.
      */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1657,27 +1657,37 @@ ReportZoneStats(const JS::ZoneStats &zSt
                       "counted under 'gc-heap/string-chars' instead.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/strings/short"),
                       zStats.gcHeapStringsShort,
                       "Memory on the garbage-collected JavaScript "
                       "heap that holds over-sized string headers, in which "
                       "string characters are stored inline.");
 
+    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/lazy-scripts"),
+                      zStats.gcHeapLazyScripts,
+                      "Memory on the garbage-collected JavaScript "
+                      "heap that represents scripts which haven't executed yet.");
+
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
                       zStats.gcHeapTypeObjects,
                       "Memory on the garbage-collected JavaScript "
                       "heap that holds type inference information.");
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/ion-codes"),
                       zStats.gcHeapIonCodes,
                       "Memory on the garbage-collected JavaScript "
                       "heap that holds references to executable code pools "
                       "used by the IonMonkey JIT.");
 
+    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts"),
+                   zStats.lazyScripts,
+                   "Memory holding miscellaneous additional information associated with lazy "
+                   "scripts.");
+
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects"),
                    zStats.typeObjects,
                    "Memory holding miscellaneous additional information associated with type "
                    "objects.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
                    zStats.typePool,
                    "Memory holding contents of type sets and related data.");
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -717,16 +717,17 @@ DescribeGCThing(bool isMarked, void *p, 
                 JS_snprintf(name, sizeof(name), "JS Object (%s)",
                             clasp->name);
             }
         } else {
             static const char trace_types[][11] = {
                 "Object",
                 "String",
                 "Script",
+                "LazyScript",
                 "IonCode",
                 "Shape",
                 "BaseShape",
                 "TypeObject",
             };
             JS_STATIC_ASSERT(NS_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1);
             JS_snprintf(name, sizeof(name), "JS %s", trace_types[traceKind]);
         }
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -191,16 +191,68 @@ class Maybe
 
     template<class T1, class T2, class T3, class T4>
     void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4) {
       MOZ_ASSERT(!constructed);
       ::new (storage.addr()) T(t1, t2, t3, t4);
       constructed = true;
     }
 
+    template<class T1, class T2, class T3, class T4, class T5>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5);
+      constructed = true;
+    }
+
+    template<class T1, class T2, class T3, class T4, class T5,
+             class T6>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
+                   const T6& t6) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5, t6);
+      constructed = true;
+    }
+
+    template<class T1, class T2, class T3, class T4, class T5,
+             class T6, class T7>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
+                   const T6& t6, const T7& t7) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7);
+      constructed = true;
+    }
+
+    template<class T1, class T2, class T3, class T4, class T5,
+             class T6, class T7, class T8>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
+                   const T6& t6, const T7& t7, const T8& t8) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8);
+      constructed = true;
+    }
+
+    template<class T1, class T2, class T3, class T4, class T5,
+             class T6, class T7, class T8, class T9>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
+                   const T6& t6, const T7& t7, const T8& t8, const T9& t9) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9);
+      constructed = true;
+    }
+
+    template<class T1, class T2, class T3, class T4, class T5,
+             class T6, class T7, class T8, class T9, class T10>
+    void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
+                   const T6& t6, const T7& t7, const T8& t8, const T9& t9, const T10& t10) {
+      MOZ_ASSERT(!constructed);
+      ::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
+      constructed = true;
+    }
+
     T* addr() {
       MOZ_ASSERT(constructed);
       return &asT();
     }
 
     T& ref() {
       MOZ_ASSERT(constructed);
       return asT();