Bug 791850 - Lazily clone self-hosted methods installed via js_DefineFunction. r=luke
authorTill Schneidereit <tschneidereit@gmail.com>
Tue, 18 Sep 2012 22:11:37 +0200
changeset 113096 862f9cd7eb0bebf5fcc4e7a6a550bfbe0b1740db
parent 113095 67fd0ece8d38020c1f9b82c14195953d682e7e7f
child 113097 3da143341145b12a1a42b46b1d669731f096436f
push id17944
push usertschneidereit@gmail.com
push dateTue, 13 Nov 2012 17:56:25 +0000
treeherdermozilla-inbound@3da143341145 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs791850
milestone19.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 791850 - Lazily clone self-hosted methods installed via js_DefineFunction. r=luke
js/src/frontend/BytecodeEmitter.cpp
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/shell/js.cpp
js/src/vm/GlobalObject.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2660,17 +2660,17 @@ frontend::EmitFunctionScript(JSContext *
     bool singleton =
         cx->typeInferenceEnabled() &&
         bce->parent &&
         bce->parent->checkSingletonContext();
 
     /* 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->script().unsafeGet());
+    JS_ASSERT(!fun->hasScript());
     fun->setScript(bce->script);
     if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
         return false;
 
     bce->tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
@@ -4830,17 +4830,17 @@ EmitFor(JSContext *cx, BytecodeEmitter *
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     AssertCanGC();
     RootedFunction fun(cx, pn->pn_funbox->function());
     JS_ASSERT(fun->isInterpreted());
-    if (fun->script().unsafeGet()) {
+    if (fun->hasScript()) {
         /*
          * This second pass is needed to emit JSOP_NOP with a source note
          * for the already-emitted function definition prolog opcode. See
          * comments in EmitStatementList.
          */
         JS_ASSERT(pn->functionIsHoisted());
         JS_ASSERT(bce->sc->isFunction);
         return EmitFunctionDefNop(cx, bce, pn->pn_index);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4990,18 +4990,18 @@ JS_DefineFunctions(JSContext *cx, JSObje
             if (!ctor) {
                 ctor = JS_GetConstructor(cx, obj);
                 if (!ctor)
                     return JS_FALSE;
             }
 
             flags &= ~JSFUN_GENERIC_NATIVE;
             JSFunction *fun = js_DefineFunction(cx, ctor, id, js_generic_native_method_dispatcher,
-                                    fs->nargs + 1, flags, NullPtr(),
-                                    JSFunction::ExtendedFinalizeKind);
+                                                fs->nargs + 1, flags,
+                                                JSFunction::ExtendedFinalizeKind);
             if (!fun)
                 return JS_FALSE;
 
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
             fun->setExtendedSlot(0, PrivateValue(fs));
@@ -5012,29 +5012,36 @@ JS_DefineFunctions(JSContext *cx, JSObje
          * self-hosted functions, as that means we're currently setting up
          * the global object that that the self-hosted code is then compiled
          * in. Self-hosted functions can access each other via their names,
          * but not via the builtin classes they get installed into.
          */
         if (fs->selfHostedName && cx->runtime->isSelfHostedGlobal(cx->global()))
             return JS_TRUE;
 
-        Rooted<PropertyName*> selfHostedPropertyName(cx);
+        /*
+         * Delay cloning self-hosted functions until they are called. This is
+         * achieved by passing js_DefineFunction a NULL JSNative which
+         * produces an interpreted JSFunction where !hasScript. Interpreted
+         * call paths then call InitializeLazyFunctionScript if !hasScript.
+         */
         if (fs->selfHostedName) {
-            JSAtom *selfHostedAtom = Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName));
-            if (!selfHostedAtom)
+            JSFunction *fun = js_DefineFunction(cx, obj, id, /* native = */ NULL, fs->nargs, 0,
+                                                JSFunction::ExtendedFinalizeKind);
+            if (!fun)
                 return JS_FALSE;
-            selfHostedPropertyName = selfHostedAtom->asPropertyName();
+            fun->setIsSelfHostedBuiltin();
+            fun->setExtendedSlot(0, PrivateValue(fs));
+        } else {
+            JSFunction *fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
+            if (!fun)
+                return JS_FALSE;
+            if (fs->call.info)
+                fun->setJitInfo(fs->call.info);
         }
-        JSFunction *fun = js_DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags,
-                                            selfHostedPropertyName);
-        if (!fun)
-            return JS_FALSE;
-        if (fs->call.info)
-            fun->setJitInfo(fs->call.info);
     }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineFunction(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -376,52 +376,72 @@ JSRuntime::initSelfHosting(JSContext *cx
 }
 
 void
 JSRuntime::markSelfHostedGlobal(JSTracer *trc)
 {
     MarkObjectRoot(trc, &selfHostedGlobal_, "self-hosting global");
 }
 
-JSFunction *
-JSRuntime::getSelfHostedFunction(JSContext *cx, Handle<PropertyName*> name)
+bool
+JSRuntime::getUnclonedSelfHostedValue(JSContext *cx, Handle<PropertyName*> name,
+                                      MutableHandleValue vp)
 {
-    RootedObject holder(cx, cx->global()->getIntrinsicsHolder());
-    RootedId id(cx, NameToId(name));
-    RootedValue funVal(cx, NullValue());
-    if (!cloneSelfHostedValueById(cx, id, holder, &funVal))
-        return NULL;
-    return funVal.toObject().toFunction();
+    RootedObject shg(cx, selfHostedGlobal_);
+    AutoCompartment ac(cx, shg);
+    return JS_GetPropertyById(cx, shg, NameToId(name), vp.address());
 }
 
 bool
-JSRuntime::cloneSelfHostedValueById(JSContext *cx, HandleId id, HandleObject holder, MutableHandleValue vp)
+JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle<PropertyName*> name,
+                                         Handle<JSFunction*> targetFun)
 {
-    Value funVal;
-    {
-        RootedObject shg(cx, selfHostedGlobal_);
-        AutoCompartment ac(cx, shg);
-        if (!JS_GetPropertyById(cx, shg, id, &funVal) || !funVal.isObject())
-            return false;
-    }
+    RootedValue funVal(cx);
+    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
+        return false;
+
+    Rooted<JSScript*> sourceScript(cx, funVal.toObject().toFunction()->script());
+    JS_ASSERT(!sourceScript->enclosingStaticScope());
+    RawScript cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript);
+    if (!cscript)
+        return false;
+    targetFun->setScript(cscript);
+    cscript->setFunction(targetFun);
+    if (!JSFunction::setTypeForScriptedFunction(cx, targetFun))
+        return false;
+
+    RootedValue targetFunVal(cx, OBJECT_TO_JSVAL(targetFun));
+    DebugOnly<bool> ok = JS_DefinePropertyById(cx, cx->global()->getIntrinsicsHolder(),
+                                               NameToId(name), targetFunVal, NULL, NULL, 0);
+    JS_ASSERT(ok);
+    return true;
+}
+
+bool
+JSRuntime::cloneSelfHostedValue(JSContext *cx, Handle<PropertyName*> name, HandleObject holder,
+                                MutableHandleValue vp)
+{
+    RootedValue funVal(cx);
+    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
+        return false;
 
     /*
      * We don't clone if we're operating in the self-hosting global, as that
      * means we're currently executing the self-hosting script while
      * initializing the runtime (see JSRuntime::initSelfHosting).
      */
     if (cx->global() == selfHostedGlobal_) {
-        vp.set(ObjectValue(funVal.toObject()));
+        vp.set(funVal);
     } else {
         RootedObject clone(cx, JS_CloneFunctionObject(cx,  &funVal.toObject(), cx->global()));
         if (!clone)
             return false;
         vp.set(ObjectValue(*clone));
     }
-    DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, vp, NULL, NULL, 0);
+    DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, NameToId(name), vp, NULL, NULL, 0);
     JS_ASSERT(ok);
     return true;
 }
 
 JSContext *
 js::NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
     JS_AbortIfWrongThread(rt);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -530,19 +530,22 @@ struct JSRuntime : js::RuntimeFriendFiel
     }
 #endif
 
     bool initSelfHosting(JSContext *cx);
     void markSelfHostedGlobal(JSTracer *trc);
     bool isSelfHostedGlobal(js::HandleObject global) {
         return global == selfHostedGlobal_;
     }
-    JSFunction *getSelfHostedFunction(JSContext *cx, js::Handle<js::PropertyName*> name);
-    bool cloneSelfHostedValueById(JSContext *cx, js::HandleId id, js::HandleObject holder,
-                                  js::MutableHandleValue vp);
+    bool getUnclonedSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
+                                    js::MutableHandleValue vp);
+    bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle<js::PropertyName*> name,
+                                       js::Handle<JSFunction*> targetFun);
+    bool cloneSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
+                              js::HandleObject holder, js::MutableHandleValue vp);
 
     /* Base address of the native stack for the current thread. */
     uintptr_t           nativeStackBase;
 
     /* The native stack size limit that runtime should not exceed. */
     size_t              nativeStackQuota;
 
     /*
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -397,17 +397,17 @@ js::DefineFunctionWithReserved(JSContext
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
-    return js_DefineFunction(cx, obj, id, call, nargs, attrs, NullPtr(), JSFunction::ExtendedFinalizeKind);
+    return js_DefineFunction(cx, obj, id, call, nargs, attrs, JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSFunction *)
 js::NewFunctionWithReserved(JSContext *cx, JSNative native, unsigned nargs, unsigned flags,
                             JSObject *parentArg, const char *name)
 {
     RootedObject parent(cx, parentArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -321,17 +321,18 @@ fun_resolve(JSContext *cx, HandleObject 
         return true;
     }
 
     if (JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().name)) {
         JS_ASSERT(!IsInternalFunctionObject(obj));
 
         RootedValue v(cx);
         if (JSID_IS_ATOM(id, cx->names().length)) {
-            uint16_t defaults = fun->isInterpreted() ? fun->script()->ndefaults : 0;
+            //FIXME: bug 810715 - deal with lazy interpreted functions with default args
+            uint16_t defaults = fun->hasScript() ? fun->script()->ndefaults : 0;
             v.setInt32(fun->nargs - defaults - fun->hasRest());
         } else {
             v.setString(fun->atom() == NULL ?  cx->runtime->emptyString : fun->atom());
         }
 
         if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return false;
@@ -478,16 +479,28 @@ js::CloneInterpretedFunction(JSContext *
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return NULL;
 
     RootedScript cloneScript(cx, clone->script());
     js_CallNewScriptHook(cx, cloneScript, clone);
     return clone;
 }
 
+bool
+js::InitializeLazyFunctionScript(JSContext *cx, HandleFunction fun)
+{
+    JS_ASSERT(fun->isLazy());
+    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, fun);
+}
+
 /*
  * [[HasInstance]] internal method for Function objects: fetch the .prototype
  * property of its 'this' parameter, and walks the prototype chain of v (only
  * if v is an object) returning true if .prototype is found.
  */
 static JSBool
 fun_hasInstance(JSContext *cx, HandleObject objArg, MutableHandleValue v, JSBool *bp)
 {
@@ -528,17 +541,17 @@ JSFunction::trace(JSTracer *trc)
         MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
                        toExtended()->extendedSlots, "nativeReserved");
     }
 
     if (atom_)
         MarkString(trc, &atom_, "atom");
 
     if (isInterpreted()) {
-        if (u.i.script_)
+        if (hasScript())
             MarkScriptUnbarriered(trc, &u.i.script_, "script");
         if (u.i.env_)
             MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
     }
 }
 
 static void
 fun_trace(JSTracer *trc, RawObject obj)
@@ -619,26 +632,27 @@ FindBody(JSContext *cx, HandleFunction f
 
 JSString *
 js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
 {
     AssertCanGC();
     StringBuffer out(cx);
     RootedScript script(cx);
 
-    if (fun->isInterpreted())
+    if (fun->hasScript()) {
         script = fun->script();
-
-    if (fun->isInterpreted() && script->isGeneratorExp) {
-        if ((!bodyOnly && !out.append("function genexp() {")) ||
-            !out.append("\n    [generator expression]\n") ||
-            (!bodyOnly && !out.append("}"))) {
-            return NULL;
+        if (script->isGeneratorExp) {
+            if ((!bodyOnly && !out.append("function genexp() {")) ||
+                !out.append("\n    [generator expression]\n") ||
+                (!bodyOnly && !out.append("}")))
+            {
+                return NULL;
+            }
+            return out.finishString();
         }
-        return out.finishString();
     }
     if (!bodyOnly) {
         // If we're not in pretty mode, put parentheses around lambda functions.
         if (fun->isInterpreted() && !lambdaParen && fun->isLambda()) {
             if (!out.append("("))
                 return NULL;
         }
         if (!out.append("function "))
@@ -1119,17 +1133,17 @@ fun_isGenerator(JSContext *cx, unsigned 
 
     RawFunction fun;
     if (!IsFunctionObject(vp[1], &fun)) {
         JS_SET_RVAL(cx, vp, BooleanValue(false));
         return true;
     }
 
     bool result = false;
-    if (fun->isInterpreted()) {
+    if (fun->hasScript()) {
         RawScript script = fun->script().get(nogc);
         JS_ASSERT(script->length != 0);
         result = script->isGenerator;
     }
 
     JS_SET_RVAL(cx, vp, BooleanValue(result));
     return true;
 }
@@ -1525,17 +1539,16 @@ js_CloneFunctionObject(JSContext *cx, Ha
         /*
          * Across compartments we have to clone the script for interpreted
          * functions. Cross-compartment cloning only happens via JSAPI
          * (JS_CloneFunctionObject) which dynamically ensures that 'script' has
          * no enclosing lexical scope (only the global scope).
          */
         if (clone->isInterpreted()) {
             RootedScript script(cx, clone->script());
-            JS_ASSERT(script);
             JS_ASSERT(script->compartment() == fun->compartment());
             JS_ASSERT_IF(script->compartment() != cx->compartment,
                          !script->enclosingStaticScope());
 
             RootedObject scope(cx, script->enclosingStaticScope());
 
             clone->mutableScript().init(NULL);
 
@@ -1553,17 +1566,17 @@ js_CloneFunctionObject(JSContext *cx, Ha
             Debugger::onNewScript(cx, script, global);
         }
     }
     return clone;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
-                  unsigned nargs, unsigned flags, Handle<PropertyName*> selfHostedName, AllocKind kind)
+                  unsigned nargs, unsigned flags, AllocKind kind)
 {
     PropertyOp gop;
     StrictPropertyOp sop;
 
     RootedFunction fun(cx);
 
     if (flags & JSFUN_STUB_GSOPS) {
         /*
@@ -1575,33 +1588,23 @@ js_DefineFunction(JSContext *cx, HandleO
         flags &= ~JSFUN_STUB_GSOPS;
         gop = JS_PropertyStub;
         sop = JS_StrictPropertyStub;
     } else {
         gop = NULL;
         sop = NULL;
     }
 
-    /*
-     * To support specifying both native and self-hosted functions using
-     * JSFunctionSpec, js_DefineFunction can be invoked with either native
-     * or selfHostedName set. It is assumed that selfHostedName is set if
-     * native isn't.
-     */
-    if (native) {
-        JS_ASSERT(!selfHostedName);
-        RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
-        JSFunction::Flags funFlags = JSAPIToJSFunctionFlags(flags);
-        fun = js_NewFunction(cx, NullPtr(), native, nargs,
-                             funFlags, obj, atom, kind);
-    } else {
-        JS_ASSERT(!cx->runtime->isSelfHostedGlobal(cx->global()));
-        fun = cx->runtime->getSelfHostedFunction(cx, selfHostedName);
-        fun->initAtom(JSID_TO_ATOM(id));
-    }
+    JSFunction::Flags funFlags;
+    if (!native)
+        funFlags = JSFunction::INTERPRETED;
+    else
+        funFlags = JSAPIToJSFunctionFlags(flags);
+    RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
+    fun = js_NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, kind);
     if (!fun)
         return NULL;
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!JSObject::defineGeneric(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return NULL;
 
     return fun;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -79,16 +79,18 @@ struct JSFunction : public JSObject
     bool isNative()                 const { return !(flags & INTERPRETED); }
 
     /* 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 isLazy()                   const { return isInterpreted() && !hasScript(); }
+    bool hasScript()                const { return isInterpreted() && u.i.script_; }
     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; }
 
@@ -162,17 +164,17 @@ struct 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_); }
 
     js::Return<JSScript*> script() const {
-        JS_ASSERT(isInterpreted());
+        JS_ASSERT(hasScript());
         return JS::HandleScript::fromMarkedLocation(&u.i.script_);
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
         return *(js::HeapPtrScript *)&u.i.script_;
     }
 
@@ -283,17 +285,17 @@ js_NewFunction(JSContext *cx, js::Handle
 
 extern JSFunction * JS_FASTCALL
 js_CloneFunctionObject(JSContext *cx, js::HandleFunction fun,
                        js::HandleObject parent, js::HandleObject proto,
                        js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, js::HandleObject obj, js::HandleId id, JSNative native,
-                  unsigned nargs, unsigned flags, js::Handle<js::PropertyName*> selfHostedName = JS::NullPtr(),
+                  unsigned nargs, unsigned flags,
                   js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 namespace js {
 
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough are that efficient
  * storage is required (no malloc'ed reserved slots).
@@ -329,16 +331,19 @@ JSString *FunctionToString(JSContext *cx
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleObject objp);
 
 extern JSObject *
 CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
 
+bool
+InitializeLazyFunctionScript(JSContext *cx, HandleFunction fun);
+
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
 ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp);
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -363,16 +363,19 @@ js::InvokeKernel(JSContext *cx, CallArgs
     }
 
     /* Invoke native functions. */
     RootedFunction fun(cx, callee.toFunction());
     JS_ASSERT_IF(construct, !fun->isNativeConstructor());
     if (fun->isNative())
         return CallJSNative(cx, fun->native(), args);
 
+    if (fun->isLazy() && !InitializeLazyFunctionScript(cx, fun))
+        return false;
+
     if (!TypeMonitorCall(cx, args, construct))
         return false;
 
     /* Get pointer to new frame/slots, prepare arguments. */
     InvokeFrameGuard ifg;
     if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
         return false;
 
@@ -2334,16 +2337,18 @@ BEGIN_CASE(JSOP_FUNCALL)
         DO_NEXT_OP(len);
     }
 
     if (!TypeMonitorCall(cx, args, construct))
         goto error;
 
     InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE;
     bool newType = cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
+    if (fun->isLazy() && !InitializeLazyFunctionScript(cx, fun))
+        goto error;
     RawScript funScript = fun->script().unsafeGet();
     if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
         goto error;
 
     SET_SCRIPT(regs.fp()->script());
 #ifdef JS_METHODJIT
     script->resetLoopCount();
 #endif
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -1015,17 +1015,17 @@ class FastInvokeGuard
 #endif
     {
         initFunction(fval);
     }
 
     void initFunction(const Value &fval) {
         if (fval.isObject() && fval.toObject().isFunction()) {
             JSFunction *fun = fval.toObject().toFunction();
-            if (fun->isInterpreted()) {
+            if (fun->hasScript()) {
                 fun_ = fun;
                 script_ = fun->script();
             }
         }
     }
 
     InvokeArgsGuard &args() {
         return args_;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5278,17 +5278,17 @@ dumpValue(const Value &v)
     else if (v.isObject() && v.toObject().isFunction()) {
         JSFunction *fun = v.toObject().toFunction();
         if (fun->displayAtom()) {
             fputs("<function ", stderr);
             FileEscapedString(stderr, fun->displayAtom(), 0);
         } else {
             fputs("<unnamed function", stderr);
         }
-        if (fun->isInterpreted()) {
+        if (fun->hasScript()) {
             JSScript *script = fun->script().get(nogc);
             fprintf(stderr, " (%s:%u)",
                     script->filename ? script->filename : "", script->lineno);
         }
         fprintf(stderr, " at %p>", (void *) fun);
     } else if (v.isObject()) {
         JSObject *obj = &v.toObject();
         Class *clasp = obj->getClass();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1108,17 +1108,17 @@ js_NewPrinter(JSContext *cx, const char 
     jp->grouped = !!grouped;
     jp->strict = !!strict;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->pcstack = NULL;
     jp->fun = fun;
     jp->localNames = NULL;
     jp->decompiledOpcodes = NULL;
-    if (fun && fun->isInterpreted()) {
+    if (fun && fun->hasScript()) {
         if (!SetPrinterLocalNames(cx, fun->script().unsafeGet(), jp)) {
             js_DestroyPrinter(jp);
             return NULL;
         }
     }
     return jp;
 }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1836,30 +1836,28 @@ JSScript::isShortRunning()
            && getMaxLoopCount() < 40
 #endif
            ;
 }
 
 bool
 JSScript::enclosingScriptsCompiledSuccessfully() const
 {
-    AutoAssertNoGC nogc;
-
     /*
      * When a nested script is succesfully compiled, it is eagerly given the
      * static JSFunction of its enclosing script. The enclosing function's
      * '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.
      */
     RawObject enclosing = enclosingScope_;
     while (enclosing) {
         if (enclosing->isFunction()) {
             RawFunction fun = enclosing->toFunction();
-            if (!fun->script().get(nogc))
+            if (!fun->hasScript())
                 return false;
             enclosing = fun->script()->enclosingScope_;
         } else {
             enclosing = enclosing->asStaticBlock().enclosingStaticScope();
         }
     }
     return true;
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2361,17 +2361,17 @@ static JSObject *
 LambdaIsGetElem(JSObject &lambda)
 {
     AutoAssertNoGC nogc;
 
     if (!lambda.isFunction())
         return NULL;
 
     JSFunction *fun = lambda.toFunction();
-    if (!fun->isInterpreted())
+    if (!fun->hasScript())
         return NULL;
 
     RawScript script = fun->script().get(nogc);
     jsbytecode *pc = script->code;
 
     /*
      * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
      * Rule out the (unlikely) possibility of a heavyweight function since it
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -279,16 +279,20 @@ ShouldJaegerCompileCallee(JSContext *cx,
 static inline bool
 UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
                    void **pret, bool *unjittable, uint32_t argc)
 {
     AssertCanGC();
     JSContext *cx = f.cx;
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     RootedFunction newfun(cx, args.callee().toFunction());
+
+    if (newfun->isLazy() && !InitializeLazyFunctionScript(cx, newfun))
+        return false;
+
     RootedScript newscript(cx, newfun->script());
 
     bool construct = InitialFrameFlagsAreConstructing(initial);
 
     RootedScript fscript(cx, f.script());
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, fscript, f.pc());
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2207,17 +2207,17 @@ Clone(JSContext *cx, unsigned argc, jsva
             JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
             if (!fun)
                 return false;
             funobj = JS_GetFunctionObject(fun);
         }
     }
     if (funobj->compartment() != cx->compartment) {
         JSFunction *fun = funobj->toFunction();
-        if (fun->isInterpreted() && fun->script()->compileAndGo) {
+        if (fun->hasScript() && fun->script()->compileAndGo) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
                                  "function", "compile-and-go");
             return false;
         }
     }
 
     if (argc > 1) {
         if (!JS_ValueToObject(cx, argv[1], parent.address()))
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -387,23 +387,18 @@ class GlobalObject : public JSObject
         return HasDataProperty(cx, self, NameToId(name), &fun);
     }
 
     bool getIntrinsicValue(JSContext *cx, PropertyName *name, MutableHandleValue value) {
         RootedObject holder(cx, &getSlotRef(INTRINSICS).toObject());
         RootedId id(cx, NameToId(name));
         if (HasDataProperty(cx, holder, id, value.address()))
             return true;
-        bool ok = cx->runtime->cloneSelfHostedValueById(cx, id, holder, value);
-        if (!ok)
-            return false;
-
-        ok = JS_DefinePropertyById(cx, holder, id, value, NULL, NULL, 0);
-        JS_ASSERT(ok);
-        return true;
+        Rooted<PropertyName*> rootedName(cx, name);
+        return cx->runtime->cloneSelfHostedValue(cx, rootedName, holder, value);
     }
 
     inline RegExpStatics *getRegExpStatics() const;
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }