Bug 886193 - Part 2: Introduce JSScript::nonDelazifyingFunction and use it whereever possible. r=jandem
authorTill Schneidereit <till@tillschneidereit.net>
Thu, 09 Jan 2014 17:13:25 +0100
changeset 178946 0292b2de36c0dede739ce870f47fe8eeb72ab688
parent 178945 2984d53fe0cf45004ddc0d24deff8897dba0b8d6
child 178947 508ad49212c05a35643656ac554ba7a480a48020
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs886193
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 886193 - Part 2: Introduce JSScript::nonDelazifyingFunction and use it whereever possible. r=jandem
js/jsd/jsd_scpt.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineFrame.h
js/src/jit/BaselineFrameInfo.h
js/src/jit/BaselineJIT.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CompileInfo.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonFrames.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/shared/BaselineCompiler-shared.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jscompartment.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/jsworkers.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ForkJoin.cpp
js/src/vm/Interpreter.cpp
js/src/vm/OldDebugAPI.cpp
js/src/vm/SPSProfiler.h
js/src/vm/ScopeObject.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/jsd/jsd_scpt.cpp
+++ b/js/jsd/jsd_scpt.cpp
@@ -355,17 +355,17 @@ JSScript *
 jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
 {
     return script->script;
 }
 
 JSFunction *
 jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
 {
-    AutoSafeJSContext cx; // NB: Actually unused.
+    AutoSafeJSContext cx;
     return JS_GetScriptFunction(cx, script->script);
 }
 
 JSDScript*
 jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
 {
     JSDScript *jsdscript = *iterp;
     
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -234,20 +234,18 @@ frontend::CompileScript(ExclusiveContext
     Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true,
                                     canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr);
     parser.sct = sct;
     parser.ss = ss;
 
     Directives directives(options.strictOption);
     GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption);
 
-    bool savedCallerFun =
-        options.compileAndGo &&
-        evalCaller &&
-        (evalCaller->function() || evalCaller->savedCallerFun());
+    bool savedCallerFun = options.compileAndGo &&
+                          evalCaller && evalCaller->functionOrCallerFunction();
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
                                                   options, staticLevel, sourceObject, 0, length));
     if (!script)
         return nullptr;
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).
     InternalHandle<Bindings*> bindings(script, &script->bindings);
@@ -403,17 +401,17 @@ frontend::CompileScript(ExclusiveContext
         return nullptr;
 
     return script;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const jschar *chars, size_t length)
 {
-    JS_ASSERT(cx->compartment() == lazy->function()->compartment());
+    JS_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
     CompileOptions options(cx, lazy->version());
     options.setPrincipals(cx->compartment()->principals)
            .setOriginPrincipals(lazy->originPrincipals())
            .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setCompileAndGo(true)
            .setNoScriptRval(false)
@@ -426,17 +424,17 @@ frontend::CompileLazyFunction(JSContext 
                                 options);
 #endif
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, nullptr, lazy);
 
     uint32_t staticLevel = lazy->staticLevel(cx);
 
-    Rooted<JSFunction*> fun(cx, lazy->function());
+    Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     JS_ASSERT(!lazy->isLegacyGenerator());
     ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
                                                   lazy->generatorKind());
     if (!pn)
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1280,17 +1280,17 @@ TryConvertFreeName(BytecodeEmitter *bce,
                     // Use generic ops if a catch block is encountered.
                     return false;
                 }
                 if (ssi.hasDynamicScopeObject())
                     hops++;
                 continue;
             }
             RootedScript script(bce->sc->context, ssi.funScript());
-            if (script->function()->atom() == pn->pn_atom)
+            if (script->functionNonDelazifying()->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;
@@ -1862,17 +1862,17 @@ BytecodeEmitter::needsImplicitThis()
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx)
 {
     // Note: when parsing off thread the resulting scripts need to be handed to
     // the debugger after rejoining to the main thread.
     if (!cx->isJSContext())
         return;
 
-    RootedFunction function(cx, script->function());
+    RootedFunction function(cx, script->functionNonDelazifying());
     CallNewScriptHook(cx->asJSContext(), script, function);
     // Lazy scripts are never top level (despite always being invoked with a
     // nullptr parent), and so the hook should never be fired.
     if (emitterMode != LazyFunction && !parent) {
         GlobalObject *compileAndGoGlobal = nullptr;
         if (script->compileAndGo())
             compileAndGoGlobal = &script->global();
         Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal);
@@ -2790,17 +2790,17 @@ frontend::EmitFunctionScript(ExclusiveCo
      * initializers created within it may be given more precise types.
      */
     if (runOnce) {
         bce->script->setTreatAsRunOnce();
         JS_ASSERT(!bce->script->hasRunOnce());
     }
 
     /* Initialize fun->script() so that the debugger has a valid fun->script(). */
-    RootedFunction fun(cx, bce->script->function());
+    RootedFunction fun(cx, bce->script->functionNonDelazifying());
     JS_ASSERT(fun->isInterpreted());
 
     if (fun->isInterpretedLazy()) {
         AutoLockForCompilation lock(cx);
         fun->setUnlazifiedScript(bce->script);
     } else {
         fun->setScript(bce->script);
     }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -108,18 +108,18 @@ BaselineCompiler::compile()
         return Method_Error;
 
     Linker linker(masm);
     JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE);
     if (!code)
         return Method_Error;
 
     JSObject *templateScope = nullptr;
-    if (script->function()) {
-        RootedFunction fun(cx, script->function());
+    if (script->functionNonDelazifying()) {
+        RootedFunction fun(cx, script->functionNonDelazifying());
         if (fun->isHeavyweight()) {
             templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap);
             if (!templateScope)
                 return Method_Error;
 
             if (fun->isNamedLambda()) {
                 RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
                 if (!declEnvObject)
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -177,17 +177,17 @@ class BaselineFrame
     }
 
     unsigned numActualArgs() const {
         return *(size_t *)(reinterpret_cast<const uint8_t *>(this) +
                              BaselineFrame::Size() +
                              offsetOfNumActualArgs());
     }
     unsigned numFormalArgs() const {
-        return script()->function()->nargs();
+        return script()->functionNonDelazifying()->nargs();
     }
     Value &thisValue() const {
         return *(Value *)(reinterpret_cast<const uint8_t *>(this) +
                          BaselineFrame::Size() +
                          offsetOfThis());
     }
     Value *argv() const {
         return (Value *)(reinterpret_cast<const uint8_t *>(this) +
--- a/js/src/jit/BaselineFrameInfo.h
+++ b/js/src/jit/BaselineFrameInfo.h
@@ -177,17 +177,17 @@ class FrameInfo
     { }
 
     bool init(TempAllocator &alloc);
 
     uint32_t nlocals() const {
         return script->nfixed();
     }
     uint32_t nargs() const {
-        return script->function()->nargs();
+        return script->functionNonDelazifying()->nargs();
     }
 
   private:
     inline StackValue *rawPush() {
         StackValue *val = &stack[spIndex++];
         val->reset();
         return val;
     }
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -215,16 +215,18 @@ jit::EnterBaselineAtBranch(JSContext *cx
 MethodStatus
 jit::BaselineCompile(JSContext *cx, HandleScript script)
 {
     JS_ASSERT(!script->hasBaselineScript());
     JS_ASSERT(script->canBaselineCompile());
 
     LifoAlloc alloc(BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
 
+    script->ensureNonLazyCanonicalFunction(cx);
+
     TempAllocator *temp = alloc.new_<TempAllocator>(&alloc);
     if (!temp)
         return Method_Error;
 
     IonContext ictx(cx, temp);
 
     BaselineCompiler compiler(cx, *temp, script);
     if (!compiler.init())
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3396,17 +3396,18 @@ bool
 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
 {
     Register obj = ToRegister(lir->output());
     JSObject *templateObj = lir->mir()->templateObj();
     CompileInfo &info = lir->mir()->block()->info();
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
-                                   (ArgList(), ImmGCPtr(info.fun()), Imm32(gc::DefaultHeap)),
+                                   (ArgList(), ImmGCPtr(info.funMaybeLazy()),
+                                    Imm32(gc::DefaultHeap)),
                                    StoreRegisterTo(obj));
     if (!ool)
         return false;
 
     masm.newGCThing(obj, templateObj, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(obj, templateObj);
     masm.bind(ool->rejoin());
     return true;
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -46,20 +46,22 @@ class CompileInfo
   public:
     CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
                 ExecutionMode executionMode, bool scriptNeedsArgsObj)
       : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
         executionMode_(executionMode), scriptNeedsArgsObj_(scriptNeedsArgsObj)
     {
         JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
 
-        // The function here can flow in from anywhere so look up the canonical function to ensure that
-        // we do not try to embed a nursery pointer in jit-code.
+        // The function here can flow in from anywhere so look up the canonical
+        // function to ensure that we do not try to embed a nursery pointer in
+        // jit-code. Precisely because it can flow in from anywhere, it's not
+        // guaranteed to be non-lazy. Hence, don't access its script!
         if (fun_) {
-            fun_ = fun_->nonLazyScript()->function();
+            fun_ = fun_->nonLazyScript()->functionNonDelazifying();
             JS_ASSERT(fun_->isTenured());
         }
 
         nimplicit_ = StartArgSlot(script)                   /* scope chain and argument obj */
                    + (fun ? 1 : 0);                         /* this */
         nargs_ = fun ? fun->nargs() : 0;
         nlocals_ = script->nfixed();
         nstack_ = script->nslots() - script->nfixed();
@@ -75,17 +77,17 @@ class CompileInfo
         nlocals_ = nlocals;
         nstack_ = 1;  /* For FunctionCompiler::pushPhiInput/popPhiOutput */
         nslots_ = nlocals_ + nstack_;
     }
 
     JSScript *script() const {
         return script_;
     }
-    JSFunction *fun() const {
+    JSFunction *funMaybeLazy() const {
         return fun_;
     }
     bool constructing() const {
         return constructing_;
     }
     jsbytecode *osrPc() {
         return osrPc_;
     }
@@ -169,17 +171,17 @@ class CompileInfo
         JS_ASSERT(script());
         return 1;
     }
     uint32_t argsObjSlot() const {
         JS_ASSERT(hasArguments());
         return 2;
     }
     uint32_t thisSlot() const {
-        JS_ASSERT(fun());
+        JS_ASSERT(funMaybeLazy());
         JS_ASSERT(nimplicit_ > 0);
         return nimplicit_ - 1;
     }
     uint32_t firstArgSlot() const {
         return nimplicit_;
     }
     uint32_t argSlotUnchecked(uint32_t i) const {
         // During initialization, some routines need to get at arg
@@ -208,26 +210,26 @@ class CompileInfo
     }
 
     uint32_t startArgSlot() const {
         JS_ASSERT(script());
         return StartArgSlot(script());
     }
     uint32_t endArgSlot() const {
         JS_ASSERT(script());
-        return CountArgSlots(script(), fun());
+        return CountArgSlots(script(), funMaybeLazy());
     }
 
     uint32_t totalSlots() const {
-        JS_ASSERT(script() && fun());
+        JS_ASSERT(script() && funMaybeLazy());
         return nimplicit() + nargs() + nlocals();
     }
 
     bool isSlotAliased(uint32_t index) const {
-        if (fun() && index == thisSlot())
+        if (funMaybeLazy() && index == thisSlot())
             return false;
 
         uint32_t arg = index - firstArgSlot();
         if (arg < nargs()) {
             if (script()->formalIsAliased(arg))
                 return true;
             return false;
         }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1617,17 +1617,19 @@ TrackAllProperties(JSContext *cx, JSObje
 
 static void
 TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame)
 {
     // Ensure that all properties of singleton call objects which the script
     // could access are tracked. These are generally accessed through
     // ALIASEDVAR operations in baseline and will not be tracked even if they
     // have been accessed in baseline code.
-    JSObject *environment = script->function() ? script->function()->environment() : nullptr;
+    JSObject *environment = script->functionNonDelazifying()
+                            ? script->functionNonDelazifying()->environment()
+                            : nullptr;
 
     while (environment && !environment->is<GlobalObject>()) {
         if (environment->is<CallObject>() && environment->hasSingletonType())
             TrackAllProperties(cx, environment);
         environment = environment->enclosingScope();
     }
 
     if (baselineFrame) {
@@ -1646,16 +1648,20 @@ IonCompile(JSContext *cx, JSScript *scri
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
     JS_ASSERT(optimizationLevel > Optimization_DontCompile);
 
+    // Make sure the script's canonical function isn't lazy. We can't de-lazify
+    // it in a worker thread.
+    script->ensureNonLazyCanonicalFunction(cx);
+
     TrackPropertiesForSingletonScopes(cx, script, baselineFrame);
 
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
     if (!alloc)
         return AbortReason_Alloc;
 
     ScopedJSDeletePtr<LifoAlloc> autoDelete(alloc);
 
@@ -1672,18 +1678,19 @@ IonCompile(JSContext *cx, JSScript *scri
 
     if (!cx->compartment()->jitCompartment()->ensureIonStubsExist(cx))
         return AbortReason_Alloc;
 
     MIRGraph *graph = alloc->new_<MIRGraph>(temp);
     if (!graph)
         return AbortReason_Alloc;
 
-    CompileInfo *info = alloc->new_<CompileInfo>(script, script->function(), osrPc, constructing,
-                                                 executionMode, script->needsArgsObj());
+    CompileInfo *info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc,
+                                                 constructing, executionMode,
+                                                 script->needsArgsObj());
     if (!info)
         return AbortReason_Alloc;
 
     BaselineInspector *inspector = alloc->new_<BaselineInspector>(script);
     if (!inspector)
         return AbortReason_Alloc;
 
     BaselineFrameInspector *baselineFrameInspector = nullptr;
@@ -2213,17 +2220,17 @@ jit::CanEnterUsingFastInvoke(JSContext *
     JS_ASSERT(jit::IsIonEnabled(cx));
 
     // Skip if the code is expected to result in a bailout.
     if (!script->hasIonScript() || script->ionScript()->bailoutExpected())
         return Method_Skipped;
 
     // Don't handle arguments underflow, to make this work we would have to pad
     // missing arguments with |undefined|.
-    if (numActualArgs < script->function()->nargs())
+    if (numActualArgs < script->functionNonDelazifying()->nargs())
         return Method_Skipped;
 
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return Method_Error;
 
     // This can GC, so afterward, script->ion is not guaranteed to be valid.
     if (!cx->runtime()->jitRuntime()->enterIon())
         return Method_Error;
@@ -2274,17 +2281,17 @@ EnterIon(JSContext *cx, EnterJitData &da
 
 bool
 jit::SetEnterJitData(JSContext *cx, EnterJitData &data, RunState &state, AutoValueVector &vals)
 {
     data.osrFrame = nullptr;
 
     if (state.isInvoke()) {
         CallArgs &args = state.asInvoke()->args();
-        unsigned numFormals = state.script()->function()->nargs();
+        unsigned numFormals = state.script()->functionNonDelazifying()->nargs();
         data.constructing = state.asInvoke()->constructing();
         data.numActualArgs = args.length();
         data.maxArgc = Max(args.length(), numFormals) + 1;
         data.scopeChain = nullptr;
         data.calleeToken = CalleeToToken(&args.callee().as<JSFunction>());
 
         if (data.numActualArgs >= numFormals) {
             data.maxArgv = args.base() + 1;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -211,17 +211,17 @@ IsPhiObservable(MPhi *phi, Observability
                 !iter->consumer()->toDefinition()->isPhi())
                 return true;
         }
         break;
     }
 
     uint32_t slot = phi->slot();
     CompileInfo &info = phi->block()->info();
-    JSFunction *fun = info.fun();
+    JSFunction *fun = info.funMaybeLazy();
 
     // If the Phi is of the |this| value, it must always be observable.
     if (fun && slot == info.thisSlot())
         return true;
 
     // If the function may need an arguments object, then make sure to preserve
     // the scope chain, because it may be needed to construct the arguments
     // object during bailout.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -70,17 +70,17 @@ jit::NewBaselineFrameInspector(TempAlloc
 
     inspector->thisType = types::GetValueType(frame->thisValue());
 
     if (frame->scopeChain()->hasSingletonType())
         inspector->singletonScopeChain = frame->scopeChain();
 
     JSScript *script = frame->script();
 
-    if (script->function()) {
+    if (script->functionNonDelazifying()) {
         if (!inspector->argTypes.reserve(frame->numFormalArgs()))
             return nullptr;
         for (size_t i = 0; i < frame->numFormalArgs(); i++) {
             if (script->formalIsAliased(i))
                 inspector->argTypes.infallibleAppend(types::Type::UndefinedType());
             else if (!script->argsObjAliasesFormals())
                 inspector->argTypes.infallibleAppend(types::GetValueType(frame->unaliasedFormal(i)));
             else if (frame->hasArgsObj())
@@ -682,17 +682,17 @@ IonBuilder::build()
     // It's safe to start emitting actual IR, so now build the scope chain.
     if (!initScopeChain())
         return false;
 
     if (info().needsArgsObj() && !initArgumentsObject())
         return false;
 
     // Prevent |this| from being DCE'd: necessary for constructors.
-    if (info().fun())
+    if (info().funMaybeLazy())
         current->getSlot(info().thisSlot())->setGuard();
 
     // The type analysis phase attempts to insert unbox operations near
     // definitions of values. It also attempts to replace uses in resume points
     // with the narrower, unboxed variants. However, we must prevent this
     // replacement from happening on values in the entry snapshot. Otherwise we
     // could get this:
     //
@@ -914,29 +914,29 @@ IonBuilder::rewriteParameter(uint32_t sl
 // Apply Type Inference information to parameters early on, unboxing them if
 // they have a definitive type. The actual guards will be emitted by the code
 // generator, explicitly, as part of the function prologue.
 void
 IonBuilder::rewriteParameters()
 {
     JS_ASSERT(info().scopeChainSlot() == 0);
 
-    if (!info().fun())
+    if (!info().funMaybeLazy())
         return;
 
     for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
         MDefinition *param = current->getSlot(i);
         rewriteParameter(i, param, param->toParameter()->index());
     }
 }
 
 bool
 IonBuilder::initParameters()
 {
-    if (!info().fun())
+    if (!info().funMaybeLazy())
         return true;
 
     // If we are doing OSR on a frame which initially executed in the
     // interpreter and didn't accumulate type information, try to use that OSR
     // frame to determine possible initial types for 'this' and parameters.
 
     // For unknownProperties() tests under addType.
     lock();
@@ -985,17 +985,17 @@ IonBuilder::initScopeChain(MDefinition *
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
     if (!script()->compileAndGo())
         return abort("non-CNG global scripts are not supported");
 
     lock();
 
-    if (JSFunction *fun = info().fun()) {
+    if (JSFunction *fun = info().funMaybeLazy()) {
         if (!callee) {
             MCallee *calleeIns = MCallee::New(alloc());
             current->add(calleeIns);
             callee = calleeIns;
         }
         scope = MFunctionEnvironment::New(alloc(), callee);
         current->add(scope);
 
@@ -5293,17 +5293,17 @@ IonBuilder::jsop_eval(uint32_t argc)
     JSFunction *singleton = getSingleCallTarget(calleeTypes);
     if (!singleton)
         return abort("No singleton callee for eval()");
 
     if (script()->global().valueIsEval(ObjectValue(*singleton))) {
         if (argc != 1)
             return abort("Direct eval with more than one argument");
 
-        if (!info().fun())
+        if (!info().funMaybeLazy())
             return abort("Direct eval in global code");
 
         // The 'this' value for the outer and eval scripts must be the
         // same. This is not guaranteed if a primitive string/number/etc.
         // is passed through to the eval invoke as the primitive may be
         // boxed into different objects if accessed via 'this'.
         JSValueType type = thisTypes->getKnownTypeTag();
         if (type != JSVAL_TYPE_OBJECT && type != JSVAL_TYPE_NULL && type != JSVAL_TYPE_UNDEFINED)
@@ -5715,17 +5715,17 @@ IonBuilder::newOsrPreheader(MBasicBlock 
         if (needsArgsObj)
             argsObj = MOsrArgumentsObject::New(alloc(), entry);
         else
             argsObj = MConstant::New(alloc(), UndefinedValue());
         osrBlock->add(argsObj);
         osrBlock->initSlot(info().argsObjSlot(), argsObj);
     }
 
-    if (info().fun()) {
+    if (info().funMaybeLazy()) {
         // Initialize |this| parameter.
         MParameter *thisv = MParameter::New(alloc(), MParameter::THIS_SLOT, nullptr);
         osrBlock->add(thisv);
         osrBlock->initSlot(info().thisSlot(), thisv);
 
         // Initialize arguments.
         for (uint32_t i = 0; i < info().nargs(); i++) {
             uint32_t slot = needsArgsObj ? info().argSlotUnchecked(i) : info().argSlot(i);
@@ -5820,17 +5820,17 @@ IonBuilder::newOsrPreheader(MBasicBlock 
 
     // Finish the osrBlock.
     osrBlock->end(MGoto::New(alloc(), preheader));
     preheader->addPredecessor(alloc(), osrBlock);
     graph().setOsrBlock(osrBlock);
 
     // Wrap |this| with a guaranteed use, to prevent instruction elimination.
     // Prevent |this| from being DCE'd: necessary for constructors.
-    if (info().fun())
+    if (info().funMaybeLazy())
         preheader->getSlot(info().thisSlot())->setGuard();
 
     return preheader;
 }
 
 MBasicBlock *
 IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr)
 {
@@ -5860,17 +5860,17 @@ IonBuilder::newPendingLoopHeader(MBasicB
                 continue;
 
             MPhi *phi = block->getSlot(i)->toPhi();
 
             // Get the type from the baseline frame.
             types::Type existingType = types::Type::UndefinedType();
             uint32_t arg = i - info().firstArgSlot();
             uint32_t var = i - info().firstLocalSlot();
-            if (info().fun() && i == info().thisSlot())
+            if (info().funMaybeLazy() && i == info().thisSlot())
                 existingType = baselineFrame_->thisType;
             else if (arg < info().nargs())
                 existingType = baselineFrame_->argTypes[arg];
             else
                 existingType = baselineFrame_->varTypes[var];
 
             // Extract typeset from value.
             types::TemporaryTypeSet *typeSet =
@@ -9235,20 +9235,20 @@ IonBuilder::jsop_deffun(uint32_t index)
     current->add(deffun);
 
     return resumeAfter(deffun);
 }
 
 bool
 IonBuilder::jsop_this()
 {
-    if (!info().fun())
+    if (!info().funMaybeLazy())
         return abort("JSOP_THIS outside of a JSFunction.");
 
-    if (script()->strict() || info().fun()->isSelfHostedBuiltin()) {
+    if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
         // No need to wrap primitive |this| in strict mode or self-hosted code.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisType.isSomeObject()))
     {
@@ -9394,33 +9394,34 @@ IonBuilder::walkScopeChain(unsigned hops
 
 bool
 IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall)
 {
     JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc);
     if (!outerScript || !outerScript->treatAsRunOnce())
         return false;
 
-    types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function());
+    types::TypeObjectKey *funType =
+            types::TypeObjectKey::get(outerScript->functionNonDelazifying());
     if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED))
         return false;
 
     // The script this aliased var operation is accessing will run only once,
     // so there will be only one call object and the aliased var access can be
     // compiled in the same manner as a global access. We still need to find
     // the call object though.
 
     // Look for the call object on the current script's function's scope chain.
     // If the current script is inner to the outer script and the function has
     // singleton type then it should show up here.
 
     MDefinition *scope = current->getSlot(info().scopeChainSlot());
     scope->setImplicitlyUsedUnchecked();
 
-    JSObject *environment = script()->function()->environment();
+    JSObject *environment = script()->functionNonDelazifying()->environment();
     while (environment && !environment->is<GlobalObject>()) {
         if (environment->is<CallObject>() &&
             !environment->as<CallObject>().isForEval() &&
             environment->as<CallObject>().callee().nonLazyScript() == outerScript)
         {
             JS_ASSERT(environment->hasSingletonType());
             *pcall = environment;
             return true;
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -614,17 +614,17 @@ HandleException(ResumeFromException *rfe
                 bool popSPSFrame = cx->runtime()->spsProfiler.enabled();
                 if (invalidated)
                     popSPSFrame = ionScript->hasSPSInstrumentation();
 
                 // When profiling, each frame popped needs a notification that
                 // the function has exited, so invoke the probe that a function
                 // is exiting.
                 JSScript *script = frames.script();
-                probes::ExitScript(cx, script, script->function(), popSPSFrame);
+                probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
                 if (!frames.more())
                     break;
                 ++frames;
             }
 
             if (invalidated)
                 ionScript->decref(cx->runtime()->defaultFreeOp());
 
@@ -633,17 +633,17 @@ HandleException(ResumeFromException *rfe
             bool calledDebugEpilogue = false;
 
             HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
             // Unwind profiler pseudo-stack
             JSScript *script = iter.script();
-            probes::ExitScript(cx, script, script->function(),
+            probes::ExitScript(cx, script, script->functionNonDelazifying(),
                                iter.baselineFrame()->hasPushedSPSFrame());
             // After this point, any pushed SPS frame would have been popped if it needed
             // to be.  Unset the flag here so that if we call DebugEpilogue below,
             // it doesn't try to pop the SPS frame again.
             iter.baselineFrame()->unsetPushedSPSFrame();
  
             if (cx->compartment()->debugMode() && !calledDebugEpilogue) {
                 // If DebugEpilogue returns |true|, we have to perform a forced
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -511,23 +511,23 @@ JSObject *
 NewStringObject(JSContext *cx, HandleString str)
 {
     return StringObject::create(cx, str);
 }
 
 bool
 SPSEnter(JSContext *cx, HandleScript script)
 {
-    return cx->runtime()->spsProfiler.enter(cx, script, script->function());
+    return cx->runtime()->spsProfiler.enter(cx, script, script->functionNonDelazifying());
 }
 
 bool
 SPSExit(JSContext *cx, HandleScript script)
 {
-    cx->runtime()->spsProfiler.exit(cx, script, script->function());
+    cx->runtime()->spsProfiler.exit(cx, script, script->functionNonDelazifying());
     return true;
 }
 
 bool
 OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out)
 {
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, key, &id))
--- a/js/src/jit/shared/BaselineCompiler-shared.h
+++ b/js/src/jit/shared/BaselineCompiler-shared.h
@@ -91,17 +91,19 @@ class BaselineCompilerShared
         JS_ASSERT(!icEntries_.empty());
         ICLoadLabel loadLabel;
         loadLabel.label = label;
         loadLabel.icEntry = icEntries_.length() - 1;
         return icLoadLabels_.append(loadLabel);
     }
 
     JSFunction *function() const {
-        return script->function();
+        // Not delazifying here is ok as the function is guaranteed to have
+        // been delazified before compilation started.
+        return script->functionNonDelazifying();
     }
 
     PCMappingSlotInfo getStackTopSlotInfo() {
         JS_ASSERT(frame.numUnsyncedSlots() <= 2);
         switch (frame.numUnsyncedSlots()) {
           case 0:
             return PCMappingSlotInfo::MakeSlotInfo();
           case 1:
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -256,17 +256,17 @@ CodeGeneratorShared::encode(LSnapshot *s
 
     uint32_t startIndex = 0;
     for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end();
          it != end;
          ++it)
     {
         MResumePoint *mir = *it;
         MBasicBlock *block = mir->block();
-        JSFunction *fun = block->info().fun();
+        JSFunction *fun = block->info().funMaybeLazy();
         JSScript *script = block->info().script();
         jsbytecode *pc = mir->pc();
         uint32_t exprStack = mir->stackDepth() - block->info().ninvoke();
         snapshots_.startFrame(fun, script, pc, exprStack);
 
         // Ensure that all snapshot which are encoded can safely be used for
         // bailouts.
         DebugOnly<jsbytecode *> bailPC = pc;
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -220,17 +220,18 @@ FollowBranch(JSContext *cx, JSScript *sc
 /* Common representation of slots throughout analyses and the compiler. */
 static inline uint32_t ThisSlot() {
     return 0;
 }
 static inline uint32_t ArgSlot(uint32_t arg) {
     return 1 + arg;
 }
 static inline uint32_t LocalSlot(JSScript *script, uint32_t local) {
-    return 1 + (script->function() ? script->function()->nargs() : 0) + local;
+    return 1 + local +
+           (script->functionNonDelazifying() ? script->functionNonDelazifying()->nargs() : 0);
 }
 static inline uint32_t TotalSlots(JSScript *script) {
     return LocalSlot(script, 0) + script->nfixed();
 }
 
 static inline uint32_t StackSlot(JSScript *script, uint32_t index) {
     return TotalSlots(script) + index;
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4686,17 +4686,18 @@ JS_CompileFunction(JSContext *cx, JS::Ha
 JS_PUBLIC_API(JSString *)
 JS_DecompileScript(JSContext *cx, JSScript *scriptArg, const char *name, unsigned indent)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     RootedScript script(cx, scriptArg);
-    RootedFunction fun(cx, script->function());
+    script->ensureNonLazyCanonicalFunction(cx);
+    RootedFunction fun(cx, script->functionNonDelazifying());
     if (fun)
         return JS_DecompileFunction(cx, fun, indent);
     bool haveSource = script->scriptSource()->hasSourceData();
     if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
         return nullptr;
     return haveSource ? script->sourceData(cx) : js_NewStringCopyZ<CanGC>(cx, "[no source]");
 }
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -719,17 +719,17 @@ CreateLazyScriptsForCompartment(JSContex
     AutoObjectVector lazyFunctions(cx);
 
     // Find all live lazy scripts in the compartment, and via them all root
     // lazy functions in the compartment: those which have not been compiled
     // and which have a source object, indicating that their parent has been
     // compiled.
     for (gc::CellIter i(cx->zone(), gc::FINALIZE_LAZY_SCRIPT); !i.done(); i.next()) {
         LazyScript *lazy = i.get<LazyScript>();
-        JSFunction *fun = lazy->function();
+        JSFunction *fun = lazy->functionNonDelazifying();
         if (fun->compartment() == cx->compartment() &&
             lazy->sourceObject() && !lazy->maybeScript())
         {
             MOZ_ASSERT(fun->isInterpretedLazy());
             MOZ_ASSERT(lazy == fun->lazyScriptOrNull());
             if (!lazyFunctions.append(fun))
                 return false;
         }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1126,18 +1126,20 @@ JSFunction::createScriptForLazilyInterpr
         RootedScript script(cx, lazy->maybeScript());
 
         if (script) {
             AutoLockForCompilation lock(cx);
             fun->setUnlazifiedScript(script);
             return true;
         }
 
-        if (fun != lazy->function()) {
-            script = lazy->function()->getOrCreateScript(cx);
+        if (fun != lazy->functionNonDelazifying()) {
+            if (!lazy->functionDelazifying(cx))
+                return false;
+            script = lazy->functionNonDelazifying()->nonLazyScript();
             if (!script)
                 return false;
 
             AutoLockForCompilation lock(cx);
             fun->setUnlazifiedScript(script);
             return true;
         }
 
@@ -1157,17 +1159,17 @@ JSFunction::createScriptForLazilyInterpr
         if (script) {
             RootedObject enclosingScope(cx, lazy->enclosingScope());
             RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script));
             if (!clonedScript)
                 return false;
 
             clonedScript->setSourceObject(lazy->sourceObject());
 
-            fun->initAtom(script->function()->displayAtom());
+            fun->initAtom(script->functionNonDelazifying()->displayAtom());
             clonedScript->setFunction(fun);
 
             {
                 AutoLockForCompilation lock(cx);
                 fun->setUnlazifiedScript(clonedScript);
             }
 
             CallNewScriptHook(cx, clonedScript, fun);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -701,17 +701,17 @@ TypeScript::FreezeTypeSets(CompilerConst
     PodZero(types, count);
 
     for (size_t i = 0; i < count; i++) {
         if (!existing[i].clone(alloc, &types[i]))
             return false;
     }
 
     *pThisTypes = types + (ThisTypes(script) - existing);
-    *pArgTypes = (script->function() && script->function()->nargs())
+    *pArgTypes = (script->functionNonDelazifying() && script->functionNonDelazifying()->nargs())
                  ? (types + (ArgTypes(script, 0) - existing))
                  : nullptr;
     *pBytecodeTypes = types;
 
     constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
     return true;
 }
 
@@ -996,17 +996,19 @@ types::FinishCompilation(JSContext *cx, 
     }
 
     for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
         const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
         JS_ASSERT(entry.script->types);
 
         if (!CheckFrozenTypeSet(cx, entry.thisTypes, types::TypeScript::ThisTypes(entry.script)))
             succeeded = false;
-        unsigned nargs = entry.script->function() ? entry.script->function()->nargs() : 0;
+        unsigned nargs = entry.script->functionNonDelazifying()
+                         ? entry.script->functionNonDelazifying()->nargs()
+                         : 0;
         for (size_t i = 0; i < nargs; i++) {
             if (!CheckFrozenTypeSet(cx, &entry.argTypes[i], types::TypeScript::ArgTypes(entry.script, i)))
                 succeeded = false;
         }
         for (size_t i = 0; i < entry.script->nTypeSets(); i++) {
             if (!CheckFrozenTypeSet(cx, &entry.bytecodeTypes[i], &entry.script->types->typeArray()[i]))
                 succeeded = false;
         }
@@ -1060,33 +1062,37 @@ types::FinishDefinitePropertiesAnalysis(
     // than once. See also CheckDefinitePropertiesTypeSet.
     for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
         const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
         JSScript *script = entry.script;
         JS_ASSERT(script->types);
 
         JS_ASSERT(TypeScript::ThisTypes(script)->isSubset(entry.thisTypes));
 
-        unsigned nargs = script->function() ? script->function()->nargs() : 0;
+        unsigned nargs = entry.script->functionNonDelazifying()
+                         ? entry.script->functionNonDelazifying()->nargs()
+                         : 0;
         for (size_t j = 0; j < nargs; j++)
             JS_ASSERT(TypeScript::ArgTypes(script, j)->isSubset(&entry.argTypes[j]));
 
         for (size_t j = 0; j < script->nTypeSets(); j++)
             JS_ASSERT(script->types->typeArray()[j].isSubset(&entry.bytecodeTypes[j]));
     }
 #endif
 
     for (size_t i = 0; i < constraints->numFrozenScripts(); i++) {
         const CompilerConstraintList::FrozenScript &entry = constraints->frozenScript(i);
         JSScript *script = entry.script;
         JS_ASSERT(script->types);
 
         CheckDefinitePropertiesTypeSet(cx, entry.thisTypes, TypeScript::ThisTypes(script));
 
-        unsigned nargs = script->function() ? script->function()->nargs() : 0;
+        unsigned nargs = script->functionNonDelazifying()
+                         ? script->functionNonDelazifying()->nargs()
+                         : 0;
         for (size_t j = 0; j < nargs; j++)
             CheckDefinitePropertiesTypeSet(cx, &entry.argTypes[j], TypeScript::ArgTypes(script, j));
 
         for (size_t j = 0; j < script->nTypeSets(); j++)
             CheckDefinitePropertiesTypeSet(cx, &entry.bytecodeTypes[j], &script->types->typeArray()[j]);
     }
 }
 
@@ -2058,17 +2064,17 @@ NewObjectKind
 types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key)
 {
     /*
      * Objects created outside loops in global and eval scripts should have
      * singleton types. For now this is only done for plain objects and typed
      * arrays, but not normal arrays.
      */
 
-    if (script->function() && !script->treatAsRunOnce())
+    if (script->functionNonDelazifying() && !script->treatAsRunOnce())
         return GenericObject;
 
     if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
         return GenericObject;
 
     /*
      * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
      * indicating their boundary.
@@ -2274,18 +2280,18 @@ TypeZone::addPendingRecompile(JSContext 
 
     if (script->hasParallelIonScript())
         addPendingRecompile(cx, script->parallelIonScript()->recompileInfo());
 #endif
 
     // When one script is inlined into another the caller listens to state
     // changes on the callee's script, so trigger these to force recompilation
     // of any such callers.
-    if (script->function() && !script->function()->hasLazyType())
-        ObjectStateChange(cx, script->function()->type(), false);
+    if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyType())
+        ObjectStateChange(cx, script->functionNonDelazifying()->type(), false);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment()->types);
     JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
     JS_ASSERT(!target->singleton());
@@ -3397,17 +3403,17 @@ types::AddClearDefiniteFunctionUsesInScr
                                             JSScript *script, JSScript *calleeScript)
 {
     // Look for any uses of the specified calleeScript in type sets for
     // |script|, and add constraints to ensure that if the type sets' contents
     // change then the definite properties are cleared from the type.
     // This ensures that the inlining performed when the definite properties
     // analysis was done is stable.
 
-    TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->function()).objectKey();
+    TypeObjectKey *calleeKey = Type::ObjectType(calleeScript->functionNonDelazifying()).objectKey();
 
     unsigned count = TypeScript::NumTypeSets(script);
     StackTypeSet *typeArray = script->types->typeArray();
 
     for (unsigned i = 0; i < count; i++) {
         StackTypeSet *types = &typeArray[i];
         if (!types->unknownObject() && types->getObjectCount() == 1) {
             if (calleeKey != types->getObject(0)) {
@@ -3691,17 +3697,17 @@ JSScript::makeTypes(JSContext *cx)
     for (unsigned i = 0; i < nTypeSets(); i++)
         InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
                   InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
                   i, id());
     TypeSet *thisTypes = TypeScript::ThisTypes(this);
     InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
               InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
               id());
-    unsigned nargs = function() ? function()->nargs() : 0;
+    unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0;
     for (unsigned i = 0; i < nargs; i++) {
         TypeSet *types = TypeScript::ArgTypes(this, i);
         InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u",
                   InferSpewColor(types), types, InferSpewColorReset(),
                   i, id());
     }
 #endif
 
@@ -3975,19 +3981,19 @@ ExclusiveContext::getNewType(const Class
     TypeObjectWithNewScriptSet &newTypeObjects = compartment()->newTypeObjects;
 
     if (!newTypeObjects.initialized() && !newTypeObjects.init())
         return nullptr;
 
     // Canonicalize new functions to use the original one associated with its script.
     if (fun) {
         if (fun->hasScript())
-            fun = fun->nonLazyScript()->function();
+            fun = fun->nonLazyScript()->functionNonDelazifying();
         else if (fun->isInterpretedLazy())
-            fun = fun->lazyScript()->function();
+            fun = fun->lazyScript()->functionNonDelazifying();
         else
             fun = nullptr;
     }
 
     TypeObjectWithNewScriptSet::AddPtr p =
         newTypeObjects.lookupForAdd(TypeObjectWithNewScriptSet::Lookup(clasp, proto, fun));
     if (p) {
         TypeObject *type = p->object;
@@ -4565,35 +4571,38 @@ TypeScript::printTypes(JSContext *cx, Ha
 {
     JS_ASSERT(script->types == this);
 
     if (!script->hasBaselineScript())
         return;
 
     AutoEnterAnalysis enter(nullptr, script->compartment());
 
-    if (script->function())
+    if (script->functionNonDelazifying())
         fprintf(stderr, "Function");
     else if (script->isForEval())
         fprintf(stderr, "Eval");
     else
         fprintf(stderr, "Main");
     fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno());
 
-    if (script->function()) {
-        if (js::PropertyName *name = script->function()->name()) {
+    if (script->functionNonDelazifying()) {
+        if (js::PropertyName *name = script->functionNonDelazifying()->name()) {
             const jschar *chars = name->getChars(nullptr);
             JSString::dumpChars(chars, name->length());
         }
     }
 
     fprintf(stderr, "\n    this:");
     TypeScript::ThisTypes(script)->print();
 
-    for (unsigned i = 0; script->function() && i < script->function()->nargs(); i++) {
+    for (unsigned i = 0;
+         script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs();
+         i++)
+    {
         fprintf(stderr, "\n    arg%u:", i);
         TypeScript::ArgTypes(script, i)->print();
     }
     fprintf(stderr, "\n");
 
     for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
         PrintBytecode(cx, script, pc);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -572,17 +572,17 @@ TypeScript::ThisTypes(JSScript *script)
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
 /* static */ inline StackTypeSet *
 TypeScript::ArgTypes(JSScript *script, unsigned i)
 {
-    JS_ASSERT(i < script->function()->nargs());
+    JS_ASSERT(i < script->functionNonDelazifying()->nargs());
     JS_ASSERT(CurrentThreadCanReadCompilationData());
     return script->types->typeArray() + script->nTypeSets() + analyze::ArgSlot(i);
 }
 
 template <typename TYPESET>
 /* static */ inline TYPESET *
 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
 {
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -405,17 +405,18 @@ class BytecodeParser
     }
 
     void reportOOM() {
         allocScope_.releaseEarly();
         js_ReportOutOfMemory(cx_);
     }
 
     uint32_t numSlots() {
-        return 1 + (script_->function() ? script_->function()->nargs() : 0) + script_->nfixed();
+        return 1 + script_->nfixed() +
+               (script_->functionNonDelazifying() ? script_->functionNonDelazifying()->nargs() : 0);
     }
 
     uint32_t maximumStackDepth() {
         return script_->nslots() - script_->nfixed();
     }
 
     Bytecode& getCode(uint32_t offset) {
         JS_ASSERT(offset < script_->length());
@@ -2033,18 +2034,18 @@ js::GetPCCountScriptSummary(JSContext *c
     JSString *str = JS_NewStringCopyZ(cx, script->filename());
     if (!str || !(str = StringToSource(cx, str)))
         return nullptr;
     buf.append(str);
 
     AppendJSONProperty(buf, "line");
     NumberValueToStringBuffer(cx, Int32Value(script->lineno()), buf);
 
-    if (script->function()) {
-        JSAtom *atom = script->function()->displayAtom();
+    if (script->functionNonDelazifying()) {
+        JSAtom *atom = script->functionNonDelazifying()->displayAtom();
         if (atom) {
             AppendJSONProperty(buf, "name");
             if (!(str = StringToSource(cx, atom)))
                 return nullptr;
             buf.append(str);
         }
     }
 
@@ -2166,17 +2167,17 @@ GetPCCountJSON(JSContext *cx, const Scri
             const char *name = js_CodeName[op];
             AppendJSONProperty(buf, "name");
             buf.append('\"');
             buf.appendInflated(name, strlen(name));
             buf.append('\"');
         }
 
         {
-            ExpressionDecompiler ed(cx, script, script->function());
+            ExpressionDecompiler ed(cx, script, script->functionDelazifying());
             if (!ed.init())
                 return false;
             if (!ed.decompilePC(pc))
                 return false;
             char *text;
             if (!ed.getOutput(&text))
                 return false;
             AppendJSONProperty(buf, "text");
@@ -2277,17 +2278,17 @@ js::GetPCCountScriptContents(JSContext *
         return nullptr;
     }
 
     const ScriptAndCounts &sac = (*rt->scriptAndCountsVector)[index];
     JSScript *script = sac.script;
 
     StringBuffer buf(cx);
 
-    if (!script->function() && !script->compileAndGo())
+    if (!script->functionNonDelazifying() && !script->compileAndGo())
         return buf.finishString();
 
     {
         AutoCompartment ac(cx, &script->global());
         if (!GetPCCountJSON(cx, sac, buf))
             return nullptr;
     }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2855,17 +2855,17 @@ JSScript::markChildren(JSTracer *trc)
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
     if (sourceObject()) {
         JS_ASSERT(sourceObject()->compartment() == compartment());
         MarkObject(trc, &sourceObject_, "sourceObject");
     }
 
-    if (function())
+    if (functionNonDelazifying())
         MarkObject(trc, &function_, "function");
 
     if (enclosingScopeOrOriginalFunction_)
         MarkObject(trc, &enclosingScopeOrOriginalFunction_, "enclosing");
 
     if (IS_GC_MARKING_TRACER(trc)) {
         compartment()->mark();
 
@@ -3019,17 +3019,17 @@ js::SetFrameArgumentsObject(JSContext *c
         if (frame.unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS))
             frame.unaliasedLocal(var) = ObjectValue(*argsobj);
     }
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
 {
-    JS_ASSERT(script->function());
+    JS_ASSERT(script->functionNonDelazifying());
     JS_ASSERT(script->analyzedArgsUsage());
     JS_ASSERT(script->argumentsHasVarBinding());
 
     /*
      * It is possible that the arguments optimization has already failed,
      * everything has been fixed up, but there was an outstanding magic value
      * on the stack that has just now flowed into an apply. In this case, there
      * is nothing to do; GuardFunApplySpeculation will patch in the real
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1181,22 +1181,33 @@ class JSScript : public js::gc::Barriere
     }
     static size_t offsetOfBaselineOrIonSkipArgCheck() {
         return offsetof(JSScript, baselineOrIonSkipArgCheck);
     }
 
     /*
      * Original compiled function for the script, if it has a function.
      * nullptr for global and eval scripts.
+     * The delazifying variant ensures that the function isn't lazy, but can
+     * only be used under a compilation lock. The non-delazifying variant
+     * can be used off-thread and without the lock, but must only be used
+     * after earlier code has called ensureNonLazyCanonicalFunction and
+     * while the function can't have been relazified.
      */
-    JSFunction *function() const {
+    inline JSFunction *functionDelazifying() const;
+    JSFunction *functionNonDelazifying() const {
         js::AutoThreadSafeAccess ts(this);
         return function_;
     }
     inline void setFunction(JSFunction *fun);
+    /*
+     * Takes a compilation lock and de-lazifies the canonical function. Must
+     * be called before entering code that expects the function to be non-lazy.
+     */
+    inline void ensureNonLazyCanonicalFunction(JSContext *cx);
 
     JSFunction *originalFunction() const;
     void setIsCallsiteClone(JSObject *fun);
 
     JSFlatString *sourceData(JSContext *cx);
 
     static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked);
 
@@ -1610,17 +1621,18 @@ class LazyScript : public gc::BarrieredC
                uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
 
   public:
     static LazyScript *Create(ExclusiveContext *cx, HandleFunction fun,
                               uint32_t numFreeVariables, uint32_t numInnerFunctions,
                               JSVersion version, uint32_t begin, uint32_t end,
                               uint32_t lineno, uint32_t column);
 
-    JSFunction *function() const {
+    inline JSFunction *functionDelazifying(JSContext *cx) const;
+    JSFunction *functionNonDelazifying() const {
         return function_;
     }
 
     void initScript(JSScript *script);
     JSScript *maybeScript() {
         return script_;
     }
 
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -40,25 +40,53 @@ ScriptCounts::destroy(FreeOp *fop)
     fop->free_(pcCountsVector);
     fop->delete_(ionCounts);
 }
 
 void
 SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
                         HandleScript script, JSObject *argsobj);
 
+inline JSFunction *
+LazyScript::functionDelazifying(JSContext *cx) const
+{
+    if (function_ && !function_->getOrCreateScript(cx))
+        return nullptr;
+    return function_;
+}
+
 } // namespace js
 
+inline JSFunction *
+JSScript::functionDelazifying() const
+{
+    js::AutoThreadSafeAccess ts(this);
+    JS_ASSERT(js::CurrentThreadCanWriteCompilationData());
+    if (function_ && function_->isInterpretedLazy())
+        function_->setUnlazifiedScript(const_cast<JSScript *>(this));
+    return function_;
+}
+
 inline void
 JSScript::setFunction(JSFunction *fun)
 {
     JS_ASSERT(fun->isTenured());
     function_ = fun;
 }
 
+inline void
+JSScript::ensureNonLazyCanonicalFunction(JSContext *cx)
+{
+    // Infallibly delazify the canonical script.
+    if (function_ && function_->isInterpretedLazy()) {
+        js::AutoLockForCompilation lock(cx);
+        functionDelazifying();
+    }
+}
+
 inline JSFunction *
 JSScript::getFunction(size_t index)
 {
     JSFunction *fun = &getObject(index)->as<JSFunction>();
 #ifdef DEBUG
     JS_ASSERT_IF(fun->isNative(), IsAsmJSModuleNative(fun->native()));
 #endif
     return fun;
@@ -69,18 +97,18 @@ JSScript::getCallerFunction()
 {
     JS_ASSERT(savedCallerFun());
     return getFunction(0);
 }
 
 inline JSFunction *
 JSScript::functionOrCallerFunction()
 {
-    if (function())
-        return function();
+    if (functionNonDelazifying())
+        return functionNonDelazifying();
     if (savedCallerFun())
         return getCallerFunction();
     return nullptr;
 }
 
 inline js::RegExpObject *
 JSScript::getRegExp(size_t index)
 {
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -585,17 +585,17 @@ CallNewScriptHookForAllScripts(JSContext
                     RootedScript nested(cx, fun->nonLazyScript());
                     CallNewScriptHookForAllScripts(cx, nested);
                 }
             }
         }
     }
 
     // The global new script hook is called on every script that was compiled.
-    RootedFunction function(cx, script->function());
+    RootedFunction function(cx, script->functionNonDelazifying());
     CallNewScriptHook(cx, script, function);
 }
 
 JSScript *
 WorkerThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token)
 {
     ParseTask *parseTask = nullptr;
 
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -336,17 +336,17 @@ ArgSetter(JSContext *cx, HandleObject ob
 
     NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>();
     RootedScript script(cx, argsobj.containingScript());
 
     if (JSID_IS_INT(id)) {
         unsigned arg = unsigned(JSID_TO_INT(id));
         if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
             argsobj.setElement(cx, arg, vp);
-            if (arg < script->function()->nargs())
+            if (arg < script->functionNonDelazifying()->nargs())
                 types::TypeScript::SetArgument(cx, script, arg, vp);
             return true;
         }
     } else {
         JS_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee));
     }
 
     /*
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -708,17 +708,18 @@ js::ForkJoinOperation::compileForParalle
     // warmup iterations.
     while (true) {
         bool offMainThreadCompilationsInProgress = false;
         bool gatheringTypeInformation = false;
 
         // Walk over the worklist to check on the status of each entry.
         for (uint32_t i = 0; i < worklist_.length(); i++) {
             script = worklist_[i];
-            fun = script->function();
+            script->ensureNonLazyCanonicalFunction(cx_);
+            fun = script->functionNonDelazifying();
 
             // No baseline script means no type information, hence we
             // will not be able to compile very well.  In such cases,
             // we continue to run baseline iterations until either (1)
             // the potential callee *has* a baseline script or (2) the
             // potential callee's use count stops increasing,
             // indicating that they are not in fact a callee.
             if (!script->hasBaselineScript()) {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -385,16 +385,18 @@ ExecuteState::pushInterpreterFrame(JSCon
 
 bool
 js::RunScript(JSContext *cx, RunState &state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     SPSEntryMarker marker(cx->runtime());
 
+    state.script()->ensureNonLazyCanonicalFunction(cx);
+
 #ifdef JS_ION
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
             return false;
         if (status == jit::Method_Compiled) {
             jit::IonExecStatus status = jit::IonCannon(cx, state);
             return !IsErrorStatus(status);
@@ -1474,29 +1476,29 @@ Interpret(JSContext *cx, RunState &state
         JS_ASSERT(script->containsPC(REGS.pc));
         JS_ASSERT(REGS.stackDepth() <= script->nslots());
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->isExceptionPending() is true.
          */
         if (cx->isExceptionPending()) {
-            probes::EnterScript(cx, script, script->function(), REGS.fp());
+            probes::EnterScript(cx, script, script->functionNonDelazifying(), REGS.fp());
             goto error;
         }
     }
 
     /* State communicated between non-local jumps: */
     bool interpReturnOK;
 
     if (!activation.entryFrame()->isGeneratorFrame()) {
         if (!activation.entryFrame()->prologue(cx))
             goto error;
     } else {
-        probes::EnterScript(cx, script, script->function(), activation.entryFrame());
+        probes::EnterScript(cx, script, script->functionNonDelazifying(), activation.entryFrame());
     }
     if (JS_UNLIKELY(cx->compartment()->debugMode())) {
         JSTrapStatus status = ScriptDebugPrologue(cx, activation.entryFrame(), REGS.pc);
         switch (status) {
           case JSTRAP_CONTINUE:
             break;
           case JSTRAP_RETURN:
             ForcedReturn(cx, REGS);
@@ -1797,17 +1799,18 @@ CASE(JSOP_RETRVAL)
 #endif
 
         if (JS_UNLIKELY(cx->compartment()->debugMode()))
             interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
 
         if (!REGS.fp()->isYielding())
             REGS.fp()->epilogue(cx);
         else
-            probes::ExitScript(cx, script, script->function(), REGS.fp()->hasPushedSPSFrame());
+            probes::ExitScript(cx, script, script->functionNonDelazifying(),
+                               REGS.fp()->hasPushedSPSFrame());
 
 #if defined(JS_ION)
   jit_return_pop_frame:
 #endif
 
         activation.popInlineFrame(REGS.fp());
         SET_SCRIPT(REGS.fp()->script());
 
@@ -3447,17 +3450,18 @@ DEFAULT()
     MOZ_ASSUME_UNREACHABLE("Invalid HandleError continuation");
 
   exit:
     if (JS_UNLIKELY(cx->compartment()->debugMode()))
         interpReturnOK = ScriptDebugEpilogue(cx, REGS.fp(), REGS.pc, interpReturnOK);
     if (!REGS.fp()->isYielding())
         REGS.fp()->epilogue(cx);
     else
-        probes::ExitScript(cx, script, script->function(), REGS.fp()->hasPushedSPSFrame());
+        probes::ExitScript(cx, script, script->functionNonDelazifying(),
+                           REGS.fp()->hasPushedSPSFrame());
 
     gc::MaybeVerifyBarriers(cx, true);
 
 #if JS_TRACE_LOGGING
         TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_STOP);
 #endif
 
 #ifdef JS_ION
@@ -3603,17 +3607,17 @@ js::DefFunOperation(JSContext *cx, Handl
      */
     RootedFunction fun(cx, funArg);
     if (fun->isNative() || fun->environment() != scopeChain) {
         fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, TenuredObject);
         if (!fun)
             return false;
     } else {
         JS_ASSERT(script->compileAndGo());
-        JS_ASSERT(!script->function());
+        JS_ASSERT(!script->functionNonDelazifying());
     }
 
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
     RootedObject parent(cx, scopeChain);
@@ -3870,20 +3874,21 @@ js::RunOnceScriptPrologue(JSContext *cx,
 
     if (!script->hasRunOnce()) {
         script->setHasRunOnce();
         return true;
     }
 
     // Force instantiation of the script's function's type to ensure the flag
     // is preserved in type information.
-    if (!script->function()->getType(cx))
+    if (!script->functionNonDelazifying()->getType(cx))
         return false;
 
-    types::MarkTypeObjectFlags(cx, script->function(), types::OBJECT_FLAG_RUNONCE_INVALIDATED);
+    types::MarkTypeObjectFlags(cx, script->functionNonDelazifying(),
+                               types::OBJECT_FLAG_RUNONCE_INVALIDATED);
     return true;
 }
 
 bool
 js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id,
                               HandleObject val)
 {
     JS_ASSERT(val->isCallable());
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -539,17 +539,18 @@ JS_GetScriptOriginPrincipals(JSScript *s
     return script->originPrincipals();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSFunction *)
 JS_GetScriptFunction(JSContext *cx, JSScript *script)
 {
-    return script->function();
+    script->ensureNonLazyCanonicalFunction(cx);
+    return script->functionNonDelazifying();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
 {
     return obj->enclosingScope();
 }
 
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -319,17 +319,17 @@ class SPSInstrumentation
     /*
      * Flags entry into a JS function for the first time. Before this is called,
      * no instrumentation is emitted, but after this instrumentation is emitted.
      */
     bool push(JSContext *cx, JSScript *script, Assembler &masm, Register scratch) {
         if (!enabled())
             return true;
         const char *string = profiler_->profileString(cx, script,
-                                                      script->function());
+                                                      script->functionNonDelazifying());
         if (string == nullptr)
             return false;
         masm.spsPushFrame(profiler_, string, script, scratch);
         setPushed(script);
         return true;
     }
 
     /*
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -36,17 +36,17 @@ static JSObject *
 InnermostStaticScope(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(script->containsPC(pc));
     JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
 
     StaticBlockObject *block = script->getBlockScope(pc);
     if (block)
         return block;
-    return script->function();
+    return script->functionNonDelazifying();
 }
 
 Shape *
 js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc)
 {
     StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
     ScopeCoordinate sc(pc);
     while (true) {
@@ -2295,24 +2295,24 @@ AnalyzeEntrainedVariablesInScript(JSCont
 
     if (!remainingNames.empty()) {
         Sprinter buf(cx);
         if (!buf.init())
             return false;
 
         buf.printf("Script ");
 
-        if (JSAtom *name = script->function()->displayAtom()) {
+        if (JSAtom *name = script->functionNonDelazifying()->displayAtom()) {
             buf.putString(name);
             buf.printf(" ");
         }
 
         buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno());
 
-        if (JSAtom *name = innerScript->function()->displayAtom()) {
+        if (JSAtom *name = innerScript->functionNonDelazifying()->displayAtom()) {
             buf.putString(name);
             buf.printf(" ");
         }
 
         buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno());
 
         for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
             buf.printf(" ");
@@ -2362,17 +2362,17 @@ js::AnalyzeEntrainedVariables(JSContext 
     for (size_t i = 0; i < objects->length; i++) {
         JSObject *obj = objects->vector[i];
         if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
             JSFunction *fun = &obj->as<JSFunction>();
             RootedScript innerScript(cx, fun->getOrCreateScript(cx));
             if (!innerScript)
                 return false;
 
-            if (script->function() && script->function()->isHeavyweight()) {
+            if (script->functionDelazifying() && script->functionDelazifying()->isHeavyweight()) {
                 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
                     return false;
             }
 
             if (!AnalyzeEntrainedVariables(cx, innerScript))
                 return false;
         }
     }
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -283,16 +283,18 @@ InterpreterStack::getCallFrame(JSContext
 JS_ALWAYS_INLINE bool
 InterpreterStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                                   HandleScript script, InitialFrameFlags initial)
 {
     RootedFunction callee(cx, &args.callee().as<JSFunction>());
     JS_ASSERT(regs.sp == args.end());
     JS_ASSERT(callee->nonLazyScript() == script);
 
+    script->ensureNonLazyCanonicalFunction(cx);
+
     StackFrame *prev = regs.fp();
     jsbytecode *prevpc = regs.pc;
     Value *prevsp = regs.sp;
     JS_ASSERT(prev);
 
     LifoAlloc::Mark mark = allocator_.mark();
 
     StackFrame::Flags flags = ToFrameFlags(initial);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -259,27 +259,27 @@ StackFrame::prologue(JSContext *cx)
         RootedObject callee(cx, &this->callee());
         JSObject *obj = CreateThisForFunction(cx, callee,
                                               useNewType() ? SingletonObject : GenericObject);
         if (!obj)
             return false;
         functionThis() = ObjectValue(*obj);
     }
 
-    probes::EnterScript(cx, script, script->function(), this);
+    probes::EnterScript(cx, script, script->functionNonDelazifying(), this);
     return true;
 }
 
 void
 StackFrame::epilogue(JSContext *cx)
 {
     JS_ASSERT(!isYielding());
 
     RootedScript script(cx, this->script());
-    probes::ExitScript(cx, script, script->function(), hasPushedSPSFrame());
+    probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame());
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
             if (JS_UNLIKELY(cx->compartment()->debugMode()))
                 DebugScopes::onPopStrictEvalScope(this);
         } else if (isDirectEvalFrame()) {
             if (isDebuggerFrame())
@@ -795,17 +795,17 @@ ScriptFrameIter::isGlobalFrame() const
         break;
       case SCRIPTED:
         return interpFrame()->isGlobalFrame();
       case JIT:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame()->isGlobalFrame();
         JS_ASSERT(!script()->isForEval());
-        return !script()->function();
+        return !script()->functionNonDelazifying();
 #else
         break;
 #endif
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected state");
 }
 
 bool
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1559,17 +1559,17 @@ class ScriptFrameIter
 
     Activation *activation() const { return data_.activations_.activation(); }
 
     jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; }
     void        updatePcQuadratic();
     JSFunction *callee() const;
     Value       calleev() const;
     unsigned    numActualArgs() const;
-    unsigned    numFormalArgs() const { return script()->function()->nargs(); }
+    unsigned    numFormalArgs() const { return script()->functionNonDelazifying()->nargs(); }
     Value       unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
 
     JSObject   *scopeChain() const;
     CallObject &callObj() const;
 
     bool        hasArgsObj() const;
     ArgumentsObject &argsObj() const;