Bug 808181: Do not allow implicit conversion of Return<T>; r=jorendorff
authorTerrence Cole <terrence@mozilla.com>
Mon, 05 Nov 2012 10:53:04 -0800
changeset 112357 4cee05d5b1ce84770ad35222f7962289c323304f
parent 112356 ed1c5d55d104a435e241fa825930ed4697073874
child 112358 613bfe02f0c1096027e73238a9fcc27ee480531e
push id23812
push useremorley@mozilla.com
push dateTue, 06 Nov 2012 14:01:34 +0000
treeherdermozilla-central@f4aeed115e54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs808181
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 808181: Do not allow implicit conversion of Return<T>; r=jorendorff This will allow the compiler to catch more usage errors.
js/src/gc/Root.h
js/src/ion/Bailouts.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonCaches.cpp
js/src/ion/IonFrames.cpp
js/src/ion/IonFrames.h
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsscript.cpp
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jsworkers.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/BaseCompiler.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/ArgumentsObject-inl.h
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/SPSProfiler.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.h
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -72,16 +72,18 @@ class HandleBase {};
 
 template <typename T>
 class MutableHandleBase {};
 
 } /* namespace js */
 
 namespace JS {
 
+class AutoAssertNoGC;
+
 template <typename T> class MutableHandle;
 
 JS_FRIEND_API(void) EnterAssertNoGCScope();
 JS_FRIEND_API(void) LeaveAssertNoGCScope();
 JS_FRIEND_API(bool) InNoGCScope();
 
 /*
  * Handle provides an implicit constructor for NullPtr so that, given:
@@ -400,26 +402,26 @@ class Return
     Return(const S &ptr,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
       : ptr_(ptr)
     {}
 
     Return(NullPtr) : ptr_(NULL) {}
 
     /*
-     * |operator const T &| is the safest way to access a Return<T> without
-     * rooting it first: it will assert when used outside of an AutoAssertNoGC
-     * guard scope.
+     * |get(AutoAssertNoGC &)| is the safest way to access a Return<T> without
+     * rooting it first: it is impossible to call this method without an
+     * AutoAssertNoGC in scope, so the compiler will automatically catch any
+     * incorrect usage.
      *
      * Example:
      *     AutoAssertNoGC nogc;
-     *     RawScript script = fun->script();
+     *     RawScript script = fun->script().get(nogc);
      */
-    operator const T &() const {
-        JS_ASSERT(InNoGCScope());
+    const T &get(AutoAssertNoGC &) const {
         return ptr_;
     }
 
     /*
      * |operator->|'s result cannot be stored in a local variable, so it is safe
      * to use in a CanGC context iff no GC can occur anywhere within the same
      * expression (generally from one |;| to the next). |operator->| uses a
      * temporary object as a guard and will assert if a CanGC context is
@@ -472,16 +474,17 @@ class Return
      *
      * To be rewritten as:
      *     JS_ASSERT(fun->script() == myScript);
      *
      * Note: the new order tells C++ to use |Return<JSScript*>::operator=|
      *       instead of direct pointer comparison.
      */
     bool operator==(const T &other) { return ptr_ == other; }
+    bool operator!=(const T &other) { return ptr_ != other; }
     bool operator==(const Return<T> &other) { return ptr_ == other.ptr_; }
     bool operator==(const JS::Handle<T> &other) { return ptr_ == other.get(); }
     inline bool operator==(const Rooted<T> &other);
 };
 
 /*
  * By default, pointers should use the inheritance hierarchy to find their
  * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -80,17 +80,17 @@ GetBailedJSScript(JSContext *cx)
     AutoAssertNoGC nogc;
 
     // Just after the frame conversion, we can safely interpret the ionTop as JS
     // frame because it targets the bailed JS frame converted to an exit frame.
     IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop);
     switch (GetCalleeTokenTag(frame->calleeToken())) {
       case CalleeToken_Function: {
         JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
-        return fun->script();
+        return fun->script().get(nogc);
       }
       case CalleeToken_Script:
         return CalleeTokenToScript(frame->calleeToken());
       default:
         JS_NOT_REACHED("unexpected callee token kind");
         return NULL;
     }
 }
@@ -173,18 +173,18 @@ StackFrame::initFromBailout(JSContext *c
     }
     unsigned pcOff = iter.pcOffset();
     regs.pc = script()->code + pcOff;
 
     if (iter.resumeAfter())
         regs.pc = GetNextPc(regs.pc);
 
     IonSpew(IonSpew_Bailouts, " new PC is offset %u within script %p (line %d)",
-            pcOff, (void *)script(), PCToLineNumber(script(), regs.pc));
-    JS_ASSERT(exprStackSlots == js_ReconstructStackDepth(cx, script(), regs.pc));
+            pcOff, (void *)script().get(nogc), PCToLineNumber(script().get(nogc), regs.pc));
+    JS_ASSERT(exprStackSlots == js_ReconstructStackDepth(cx, script().get(nogc), regs.pc));
 }
 
 static StackFrame *
 PushInlinedFrame(JSContext *cx, StackFrame *callerFrame)
 {
     AssertCanGC();
 
     // Grab the callee object out of the caller's frame, which has already been restored.
@@ -658,17 +658,17 @@ ion::ThunkToInterpreter(Value *vp)
 
     if (status == Interpret_OSR) {
         // The interpreter currently does not ask to perform inline OSR, so
         // this path is unreachable.
         JS_NOT_REACHED("invalid");
 
         IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d",
                 cx->fp()->script()->filename,
-                PCToLineNumber(cx->fp()->script(), cx->regs().pc));
+                PCToLineNumber(cx->fp()->script().unsafeGet(), cx->regs().pc));
 
         // We want to OSR again. We need to avoid the problem where frequent
         // bailouts cause recursive nestings of Interpret and EnterIon. The
         // interpreter therefore shortcuts out, and now we're responsible for
         // completing the OSR inline.
         //
         // Note that we set runningInIon so that if we re-enter C++ from within
         // the inlined OSR, StackIter will know to traverse these frames.
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -144,17 +144,17 @@ IonBuilder::CFGState::LookupSwitch(jsbyt
     return state;
 }
 
 JSFunction *
 IonBuilder::getSingleCallTarget(uint32 argc, jsbytecode *pc)
 {
     AutoAssertNoGC nogc;
 
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
+    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script().get(nogc), argc, pc);
     if (!calleeTypes)
         return NULL;
 
     RawObject obj = calleeTypes->getSingleton();
     if (!obj || !obj->isFunction())
         return NULL;
 
     return obj->toFunction();
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -1624,17 +1624,17 @@ GenerateScopeChainGuard(MacroAssembler &
     AutoAssertNoGC nogc;
     if (scopeObj->isCall()) {
         // We can skip a guard on the call object if the script's bindings are
         // guaranteed to be immutable (and thus cannot introduce shadowing
         // variables).
         CallObject *callObj = &scopeObj->asCall();
         if (!callObj->isForEval()) {
             RawFunction fun = &callObj->callee();
-            RawScript script = fun->script();
+            RawScript script = fun->script().get(nogc);
             if (!script->funHasExtensibleScope)
                 return;
         }
     } else if (scopeObj->isGlobal()) {
         // If this is the last object on the scope walk, and the property we've
         // found is not configurable, then we don't need a shape guard because
         // the shape cannot be removed.
         if (shape && !shape->configurable())
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -924,17 +924,17 @@ InlineFrameIterator::findNextFrame()
 
         // Skip extra slots.
         while (si_.moreSlots())
             si_.skip();
 
         si_.nextFrame();
 
         callee_ = funval.toObject().toFunction();
-        script_ = callee_->script();
+        script_ = callee_->script().get(nogc);
         pc_ = script_->code + si_.pcOffset();
     }
 
     framesRead_++;
 }
 
 InlineFrameIterator
 InlineFrameIterator::operator++()
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -67,17 +67,17 @@ CalleeTokenToScript(CalleeToken token)
 static inline JSScript *
 ScriptFromCalleeToken(CalleeToken token)
 {
     AutoAssertNoGC nogc;
     switch (GetCalleeTokenTag(token)) {
       case CalleeToken_Script:
         return CalleeTokenToScript(token);
       case CalleeToken_Function:
-        return CalleeTokenToFunction(token)->script();
+        return CalleeTokenToFunction(token)->script().get(nogc);
     }
     JS_NOT_REACHED("invalid callee token tag");
     return NULL;
 }
 
 // In between every two frames lies a small header describing both frames. This
 // header, minimally, contains a returnAddress word and a descriptor word. The
 // descriptor describes the size and type of the previous frame, whereas the
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7074,19 +7074,19 @@ JS_DescribeScriptedCaller(JSContext *cx,
     if (lineno)
         *lineno = 0;
 
     ScriptFrameIter i(cx);
     if (i.done())
         return JS_FALSE;
 
     if (script)
-        *script = i.script();
+        *script = i.script().get(nogc);
     if (lineno)
-        *lineno = js::PCToLineNumber(i.script(), i.pc());
+        *lineno = js::PCToLineNumber(i.script().get(nogc), i.pc());
     return JS_TRUE;
 }
 
 #ifdef JS_THREADSAFE
 static PRStatus
 CallOnce(void *func)
 {
     JSInitCallback init = JS_DATA_TO_FUNC_PTR(JSInitCallback, func);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -613,17 +613,17 @@ PopulateReportBlame(JSContext *cx, JSErr
      * Walk stack until we find a frame that is associated with a non-builtin
      * rather than a builtin frame.
      */
     NonBuiltinScriptFrameIter iter(cx);
     if (iter.done())
         return;
 
     report->filename = iter.script()->filename;
-    report->lineno = PCToLineNumber(iter.script(), iter.pc(), &report->column);
+    report->lineno = PCToLineNumber(iter.script().get(nogc), iter.pc(), &report->column);
     report->originPrincipals = iter.script()->originPrincipals;
 }
 
 /*
  * We don't post an exception in this case, since doing so runs into
  * complications of pre-allocating an exception object which required
  * running the Exception class initializer early etc.
  * Instead we just invoke the errorReporter with an "Out Of Memory"
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -447,17 +447,17 @@ JS_ReleaseFunctionLocalNameArray(JSConte
 {
     cx->tempLifoAlloc().release(mark);
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
 {
     AutoAssertNoGC nogc;
-    return fun->maybeScript();
+    return fun->maybeScript().get(nogc);
 }
 
 JS_PUBLIC_API(JSNative)
 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
 {
     return fun->maybeNative();
 }
 
@@ -496,17 +496,17 @@ JS_BrokenFrameIterator(JSContext *cx, JS
     *iteratorp = Jsvalify(fp);
     return *iteratorp;
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fpArg)
 {
     AutoAssertNoGC nogc;
-    return Valueify(fpArg)->script();
+    return Valueify(fpArg)->script().get(nogc);
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fpArg)
 {
     /*
      * This API is used to compute the line number for jsd and XPConnect
      * exception handling backtraces. Once the stack gets really deep, the
@@ -541,17 +541,17 @@ JS_SetTopFrameAnnotation(JSContext *cx, 
 
     // Note that if this frame is running in Ion, the actual calling frame
     // could be inlined or a callee and thus we won't have a correct |fp|.
     // To account for this, ion::InvalidationBailout will transfer an
     // annotation from the old cx->fp() to the new top frame. This works
     // because we will never EnterIon on a frame with an annotation.
     fp->setAnnotation(annotation);
 
-    RawScript script = fp->script();
+    RawScript script = fp->script().get(nogc);
 
     ReleaseAllJITCode(cx->runtime->defaultFreeOp());
 
     // Ensure that we'll never try to compile this again.
     JS_ASSERT(!script->hasIonScript());
     script->ion = ION_DISABLED_SCRIPT;
 }
 
@@ -1010,17 +1010,17 @@ GetAtomTotalSize(JSContext *cx, JSAtom *
 
 JS_PUBLIC_API(size_t)
 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
 {
     AutoAssertNoGC nogc;
     size_t nbytes = sizeof *fun;
     nbytes += JS_GetObjectTotalSize(cx, fun);
     if (fun->isInterpreted())
-        nbytes += JS_GetScriptTotalSize(cx, fun->script());
+        nbytes += JS_GetScriptTotalSize(cx, fun->script().get(nogc));
     if (fun->displayAtom())
         nbytes += GetAtomTotalSize(cx, fun->displayAtom());
     return nbytes;
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
 {
@@ -1202,18 +1202,18 @@ js_CallContextDebugHandler(JSContext *cx
 JS_PUBLIC_API(StackDescription *)
 JS::DescribeStack(JSContext *cx, unsigned maxFrames)
 {
     AutoAssertNoGC nogc;
     Vector<FrameDescription> frames(cx);
 
     for (ScriptFrameIter i(cx); !i.done(); ++i) {
         FrameDescription desc;
-        desc.script = i.script();
-        desc.lineno = PCToLineNumber(i.script(), i.pc());
+        desc.script = i.script().get(nogc);
+        desc.lineno = PCToLineNumber(i.script().get(nogc), i.pc());
         desc.fun = i.maybeCallee();
         if (!frames.append(desc))
             return NULL;
         if (frames.length() == maxFrames)
             break;
     }
 
     StackDescription *desc = js_new<StackDescription>();
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -125,17 +125,17 @@ fun_getProperty(JSContext *cx, HandleObj
 
 #ifdef JS_ION
         AutoAssertNoGC nogc;
 
         // If this script hasn't been compiled yet, make sure it will never
         // be compiled. IonMonkey does not guarantee |f.arguments| can be
         // fully recovered, so we try to mitigate observing this behavior by
         // detecting its use early.
-        RawScript script = iter.script();
+        RawScript script = iter.script().get(nogc);
         if (!script->hasIonScript())
             ion::ForbidCompilation(cx, script);
 #endif
 
         vp.setObject(*argsobj);
         return true;
     }
 
@@ -1118,17 +1118,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()) {
-        RawScript script = fun->script();
+        RawScript script = fun->script().get(nogc);
         JS_ASSERT(script->length != 0);
         result = script->isGenerator;
     }
 
     JS_SET_RVAL(cx, vp, BooleanValue(result));
     return true;
 }
 #endif
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -679,17 +679,17 @@ UseNewTypeForClone(JSFunction *fun)
      *
      * Each instance of the innermost function will have a different wrapped
      * initialize method. We capture this, along with similar cases, by looking
      * for short scripts which use both .apply and arguments. For such scripts,
      * whenever creating a new instance of the function we both give that
      * instance a singleton type and clone the underlying script.
      */
 
-    RawScript script = fun->script();
+    RawScript script = fun->script().get(nogc);
 
     if (script->length >= 50)
         return false;
 
     if (script->hasConsts() || script->hasObjects() || script->hasRegexps() || fun->isHeavyweight())
         return false;
 
     bool hasArguments = false;
@@ -881,17 +881,17 @@ TypeScript::GetPcScript(JSContext *cx, M
 {
     AutoAssertNoGC nogc;
 #ifdef JS_ION
     if (cx->fp()->beginsIonActivation()) {
         ion::GetPcScript(cx, script, pc);
         return;
     }
 #endif
-    script.set(cx->fp()->script());
+    script.set(cx->fp()->script().get(nogc));
     *pc = cx->regs().pc;
 }
 
 /* static */ inline void
 TypeScript::MonitorOverflow(JSContext *cx)
 {
     RootedScript script(cx);
     jsbytecode *pc;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5255,16 +5255,17 @@ js_GetterOnlyPropertyStub(JSContext *cx,
  * Routines to print out values during debugging.  These are FRIEND_API to help
  * the debugger find them and to support temporarily hacking js_Dump* calls
  * into other code.
  */
 
 void
 dumpValue(const Value &v)
 {
+    AutoAssertNoGC nogc;
     if (v.isNull())
         fprintf(stderr, "null");
     else if (v.isUndefined())
         fprintf(stderr, "undefined");
     else if (v.isInt32())
         fprintf(stderr, "%d", v.toInt32());
     else if (v.isDouble())
         fprintf(stderr, "%g", v.toDouble());
@@ -5274,17 +5275,17 @@ dumpValue(const Value &v)
         JSFunction *fun = v.toObject().toFunction();
         if (fun->displayAtom()) {
             fputs("<function ", stderr);
             FileEscapedString(stderr, fun->displayAtom(), 0);
         } else {
             fputs("<unnamed function", stderr);
         }
         if (fun->isInterpreted()) {
-            JSScript *script = fun->script();
+            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();
         fprintf(stderr, "<%s%s at %p>",
@@ -5540,19 +5541,19 @@ JS_FRIEND_API(void)
 js_DumpBacktrace(JSContext *cx)
 {
     AutoAssertNoGC nogc;
     Sprinter sprinter(cx);
     sprinter.init();
     size_t depth = 0;
     for (StackIter i(cx); !i.done(); ++i, ++depth) {
         if (i.isScript()) {
-            const char *filename = JS_GetScriptFilename(cx, i.script());
-            unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
-            RawScript script = i.script();
+            const char *filename = JS_GetScriptFilename(cx, i.script().get(nogc));
+            unsigned line = JS_PCToLineNumber(cx, i.script().get(nogc), i.pc());
+            RawScript script = i.script().get(nogc);
             sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
                             depth, (i.isIon() ? 0 : i.interpFrame()), filename, line,
                             script, i.pc() - script->code);
         } else {
             sprinter.printf("#%d ???\n", depth);
         }
     }
     fprintf(stdout, "%s", sprinter.string());
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1841,17 +1841,17 @@ JSScript::enclosingScriptsCompiledSucces
      * 'script' field will be NULL until the enclosing script successfully
      * compiles. Thus, we can detect failed compilation by looking for
      * JSFunctions in the enclosingScope chain without scripts.
      */
     RawObject enclosing = enclosingScope_;
     while (enclosing) {
         if (enclosing->isFunction()) {
             RawFunction fun = enclosing->toFunction();
-            if (!fun->script())
+            if (!fun->script().get(nogc))
                 return false;
             enclosing = fun->script()->enclosingScope_;
         } else {
             enclosing = enclosing->asStaticBlock().enclosingStaticScope();
         }
     }
     return true;
 }
@@ -2109,36 +2109,36 @@ js_GetScriptLineExtent(RawScript script)
 }
 
 namespace js {
 
 unsigned
 CurrentLine(JSContext *cx)
 {
     AutoAssertNoGC nogc;
-    return PCToLineNumber(cx->fp()->script(), cx->regs().pc);
+    return PCToLineNumber(cx->fp()->script().get(nogc), cx->regs().pc);
 }
 
 void
 CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *linenop,
                                 JSPrincipals **origin)
 {
     AutoAssertNoGC nogc;
     NonBuiltinScriptFrameIter iter(cx);
 
     if (iter.done()) {
         *file = NULL;
         *linenop = 0;
         *origin = NULL;
         return;
     }
 
-    RawScript script = iter.script();
+    RawScript script = iter.script().get(nogc);
     *file = script->filename;
-    *linenop = PCToLineNumber(iter.script(), iter.pc());
+    *linenop = PCToLineNumber(iter.script().get(nogc), iter.pc());
     *origin = script->originPrincipals;
 }
 
 }  /* namespace js */
 
 template <class T>
 static inline T *
 Rebase(RawScript dst, RawScript src, T *srcp)
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -43,17 +43,17 @@ CurrentScriptFileLineOriginSlow(JSContex
 inline void
 CurrentScriptFileLineOrigin(JSContext *cx, const char **file, unsigned *linenop, JSPrincipals **origin,
                             LineOption opt = NOT_CALLED_FROM_JSOP_EVAL)
 {
     if (opt == CALLED_FROM_JSOP_EVAL) {
         AutoAssertNoGC nogc;
         JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
         JS_ASSERT(*(cx->regs().pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
-        RawScript script = cx->fp()->script();
+        RawScript script = cx->fp()->script().get(nogc);
         *file = script->filename;
         *linenop = GET_UINT16(cx->regs().pc + JSOP_EVAL_LENGTH);
         *origin = script->originPrincipals;
         return;
     }
 
     CurrentScriptFileLineOriginSlow(cx, file, linenop, origin);
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2364,17 +2364,17 @@ LambdaIsGetElem(JSObject &lambda)
 
     if (!lambda.isFunction())
         return NULL;
 
     JSFunction *fun = lambda.toFunction();
     if (!fun->isInterpreted())
         return NULL;
 
-    RawScript script = fun->script();
+    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
      * would make our scope walk off by 1.
      */
     if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->isHeavyweight())
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -83,40 +83,40 @@ js::CancelOffThreadIonCompile(JSCompartm
     if (!ion)
         return;
 
     AutoLockWorkerThreadState lock(compartment->rt);
 
     /* Cancel any pending entries for which processing hasn't started. */
     for (size_t i = 0; i < state.ionWorklist.length(); i++) {
         ion::IonBuilder *builder = state.ionWorklist[i];
-        if (CompiledScriptMatches(compartment, script, builder->script())) {
+        if (CompiledScriptMatches(compartment, script, builder->script().unsafeGet())) {
             FinishOffThreadIonCompile(builder);
             state.ionWorklist[i--] = state.ionWorklist.back();
             state.ionWorklist.popBack();
         }
     }
 
     /* Wait for in progress entries to finish up. */
     for (size_t i = 0; i < state.numThreads; i++) {
         const WorkerThread &helper = state.threads[i];
         while (helper.ionBuilder &&
-               CompiledScriptMatches(compartment, script, helper.ionBuilder->script()))
+               CompiledScriptMatches(compartment, script, helper.ionBuilder->script().unsafeGet()))
         {
             helper.ionBuilder->cancel();
             state.wait(WorkerThreadState::MAIN);
         }
     }
 
     ion::OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
     /* Cancel code generation for any completed entries. */
     for (size_t i = 0; i < compilations.length(); i++) {
         ion::IonBuilder *builder = compilations[i];
-        if (CompiledScriptMatches(compartment, script, builder->script())) {
+        if (CompiledScriptMatches(compartment, script, builder->script().unsafeGet())) {
             ion::FinishOffThreadBuilder(builder);
             compilations[i--] = compilations.back();
             compilations.popBack();
         }
     }
 }
 
 bool
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -141,17 +141,17 @@ class Assembler : public ValueAssembler
 #endif
         sps(sps),
         vmframe(vmframe),
         pc(NULL)
     {
         AutoAssertNoGC nogc;
         startLabel = label();
         if (vmframe)
-            sps->setPushed(vmframe->script());
+            sps->setPushed(vmframe->script().get(nogc));
     }
 
     Assembler(MJITInstrumentation *sps, jsbytecode **pc)
       : callPatches(SystemAllocPolicy()),
         availInCall(0),
         extraStackSpace(0),
         stackAdjust(0),
 #ifdef DEBUG
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -133,17 +133,17 @@ class LinkerHelper : public JSC::LinkBuf
         m_size = masm.size();   // must come after call to executableAllocAndCopy()!
         return pool;
     }
 
     JSC::CodeLocationLabel finalize(VMFrame &f) {
         AutoAssertNoGC nogc;
         masm.finalize(*this);
         JSC::CodeLocationLabel label = finalizeCodeAddendum();
-        Probes::registerICCode(f.cx, f.chunk(), f.script(), f.pc(),
+        Probes::registerICCode(f.cx, f.chunk(), f.script().get(nogc), f.pc(),
                                label.executableAddress(), masm.size());
         return label;
     }
 
     void maybeLink(MaybeJump jump, JSC::CodeLocationLabel label) {
         if (!jump.isSet())
             return;
         link(jump.get(), label);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -680,24 +680,24 @@ stubs::ScriptDebugEpilogue(VMFrame &f)
     if (!js::ScriptDebugEpilogue(f.cx, f.fp(), JS_TRUE))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::ScriptProbeOnlyPrologue(VMFrame &f)
 {
     AutoAssertNoGC nogc;
-    Probes::enterScript(f.cx, f.script(), f.script()->function(), f.fp());
+    Probes::enterScript(f.cx, f.script().get(nogc), f.script()->function(), f.fp());
 }
 
 void JS_FASTCALL
 stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
 {
     AutoAssertNoGC nogc;
-    Probes::exitScript(f.cx, f.script(), f.script()->function(), f.fp());
+    Probes::exitScript(f.cx, f.script().get(nogc), f.script()->function(), f.fp());
 }
 
 void JS_FASTCALL
 stubs::CrossChunkShim(VMFrame &f, void *edge_)
 {
     AssertCanGC();
     DebugOnly<CrossChunkEdge*> edge = (CrossChunkEdge *) edge_;
 
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -879,17 +879,17 @@ class CallCompiler : public BaseCompiler
         /*
          * Write the rejoin state to indicate this is a compilation call made
          * from an IC (the recompiler cannot detect calls made from ICs
          * automatically).
          */
         masm.storePtr(ImmPtr((void *) ic.frameSize.rejoinState(f.pc(), false)),
                       FrameAddress(offsetof(VMFrame, stubRejoin)));
 
-        masm.bumpStubCount(f.script(), f.pc(), Registers::tempCallReg());
+        masm.bumpStubCount(f.script().get(nogc), f.pc(), Registers::tempCallReg());
 
         /* Try and compile. On success we get back the nmap pointer. */
         void *compilePtr = JS_FUNC_TO_DATA_PTR(void *, stubs::CompileFunction);
         DataLabelPtr inlined;
         if (ic.frameSize.isStatic()) {
             masm.move(Imm32(ic.frameSize.staticArgc()), Registers::ArgReg1);
             masm.fallibleVMCall(cx->typeInferenceEnabled(),
                                 compilePtr, f.regs.pc, &inlined, ic.frameSize.staticLocalSlots());
@@ -996,17 +996,17 @@ class CallCompiler : public BaseCompiler
         RegisterID t0 = tempRegs.takeAnyReg().reg();
 
         /* Guard that it's actually a function object. */
         Jump claspGuard = masm.testObjClass(Assembler::NotEqual, ic.funObjReg, t0, &FunctionClass);
 
         /* Guard that it's the same script. */
         Address scriptAddr(ic.funObjReg, JSFunction::offsetOfNativeOrScript());
         Jump funGuard = masm.branchPtr(Assembler::NotEqual, scriptAddr,
-                                       ImmPtr(obj->toFunction()->script()));
+                                       ImmPtr(obj->toFunction()->script().get(nogc)));
         Jump done = masm.jump();
 
         LinkerHelper linker(masm, JSC::JAEGER_CODE);
         JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ClosureStub);
         if (!ep)
             return false;
 
         ic.hasJsFunCheck = true;
@@ -1256,17 +1256,17 @@ class CallCompiler : public BaseCompiler
                     THROWV(NULL);
             }
 #endif
             return NULL;
         }
 
         AutoAssertNoGC nogc;
         JS_ASSERT(fun);
-        JSScript *script = fun->script();
+        JSScript *script = fun->script().get(nogc);
         JS_ASSERT(script);
 
         uint32_t flags = callingNew ? StackFrame::CONSTRUCTING : 0;
 
         if (!ic.hit) {
             ic.hit = true;
             return ucr.codeAddr;
         }
@@ -1427,17 +1427,17 @@ void
 ic::GenerateArgumentCheckStub(VMFrame &f)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(f.cx->typeInferenceEnabled());
 
     JITScript *jit = f.jit();
     StackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
-    JSScript *script = fun->script();
+    JSScript *script = fun->script().get(nogc);
 
     if (jit->argsCheckPool)
         jit->resetArgsCheck();
 
     Assembler masm;
     Vector<Jump> mismatches(f.cx);
 
     if (!f.fp()->isConstructing()) {
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1075,17 +1075,17 @@ class GetPropCompiler : public PICStubCo
         if (tempRegs.hasReg(pic.objReg)) {
             tempRegs.takeReg(pic.objReg);
         } else {
             holdObjReg = tempRegs.takeAnyReg().reg();
             masm.move(pic.objReg, holdObjReg);
         }
 
         RegisterID t0 = tempRegs.takeAnyReg().reg();
-        masm.bumpStubCount(f.script(), f.pc(), t0);
+        masm.bumpStubCount(f.script().get(nogc), f.pc(), t0);
 
         /*
          * Use three values above sp on the stack for use by the call to store
          * the object and id being passed into the call as handles and to store
          * the resulting value. Temporary slots are used by GETPROP for this,
          * plus there is extra room on the stack reserved for a callee frame.
          */
         int32_t initialFrameDepth = f.regs.sp - f.fp()->slots() + 3;
@@ -1189,17 +1189,17 @@ class GetPropCompiler : public PICStubCo
         if (tempRegs.hasReg(pic.objReg)) {
             tempRegs.takeReg(pic.objReg);
         } else {
             holdObjReg = tempRegs.takeAnyReg().reg();
             masm.move(pic.objReg, holdObjReg);
         }
 
         RegisterID t0 = tempRegs.takeAnyReg().reg();
-        masm.bumpStubCount(f.script(), f.pc(), t0);
+        masm.bumpStubCount(f.script().get(nogc), f.pc(), t0);
 
         /*
          * A JSNative has the following signature:
          *
          *   JSBool native(JSContext *cx, unsigned argc, Value *vp);
          *
          * Since we are calling a getter, argc is always 0. vp must point to two
          * values, the callee and the holder. We use vp == sp to avoid clobbering
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -343,17 +343,17 @@ ClearAllFrames(JSCompartment *compartmen
 
     for (VMFrame *f = compartment->rt->jaegerRuntime().activeFrame();
          f != NULL;
          f = f->previous)
     {
         if (f->entryfp->compartment() != compartment)
             continue;
 
-        Recompiler::patchFrame(compartment, f, f->fp()->script());
+        Recompiler::patchFrame(compartment, f, f->fp()->script().get(nogc));
 
         // Clear ncode values from all frames associated with the VMFrame.
         // Patching the VMFrame's return address will cause all its frames to
         // finish in the interpreter, unless the interpreter enters one of the
         // intermediate frames at a loop boundary (where EnterMethodJIT will
         // overwrite ncode). However, leaving stale values for ncode in stack
         // frames can confuse the recompiler, which may see the VMFrame before
         // it has resumed execution.
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -832,17 +832,17 @@ stubs::TriggerIonCompile(VMFrame &f)
 }
 #endif
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
     AutoAssertNoGC nogc;
     ExpandInlineFrames(f.cx->compartment);
-    Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), f.script());
+    Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), f.script().get(nogc));
     f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
     Value rval;
 
@@ -1300,17 +1300,17 @@ FindNativeCode(VMFrame &f, jsbytecode *t
     return NULL;
 }
 
 void * JS_FASTCALL
 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
 {
     AutoAssertNoGC nogc;
     jsbytecode *jpc = pc;
-    JSScript *script = f.fp()->script();
+    JSScript *script = f.fp()->script().get(nogc);
 
     /* This is correct because the compiler adjusts the stack beforehand. */
     Value lval = f.regs.sp[-1];
 
     if (!lval.isPrimitive())
         return FindNativeCode(f, pc + GET_JUMP_OFFSET(pc));
 
     JS_ASSERT(pc[0] == JSOP_LOOKUPSWITCH);
@@ -1626,17 +1626,17 @@ stubs::CheckArgumentTypes(VMFrame &f)
 
 #ifdef DEBUG
 void JS_FASTCALL
 stubs::AssertArgumentTypes(VMFrame &f)
 {
     AutoAssertNoGC nogc;
     StackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
-    RawScript script = fun->script();
+    RawScript script = fun->script().get(nogc);
 
     /*
      * Don't check the type of 'this' for constructor frames, the 'this' value
      * has not been constructed yet.
      */
     if (!fp->isConstructing()) {
         Type type = GetValueType(f.cx, fp->thisValue());
         if (!TypeScript::ThisTypes(script)->hasType(type))
@@ -1671,17 +1671,17 @@ stubs::InvariantFailure(VMFrame &f, void
      * recompilation we will return to the call's rejoin point.
      */
     void *repatchCode = f.scratch;
     JS_ASSERT(repatchCode);
     void **frameAddr = f.returnAddressLocation();
     *frameAddr = repatchCode;
 
     /* Recompile the outermost script, and don't hoist any bounds checks. */
-    RawScript script = f.fp()->script();
+    RawScript script = f.fp()->script().get(nogc);
     JS_ASSERT(!script->failedBoundsCheck);
     script->failedBoundsCheck = true;
 
     ExpandInlineFrames(f.cx->compartment);
 
     mjit::Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), script);
     mjit::ReleaseScriptCode(f.cx->runtime->defaultFreeOp(), script);
 
--- a/js/src/vm/ArgumentsObject-inl.h
+++ b/js/src/vm/ArgumentsObject-inl.h
@@ -69,33 +69,33 @@ ArgumentsObject::setArg(unsigned i, cons
 inline const Value &
 ArgumentsObject::element(uint32_t i) const
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(!isElementDeleted(i));
     const Value &v = data()->args[i];
     if (v.isMagic(JS_FORWARD_TO_CALL_OBJECT)) {
         CallObject &callobj = getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall();
-        for (AliasedFormalIter fi(callobj.callee().script()); ; fi++) {
+        for (AliasedFormalIter fi(callobj.callee().script().get(nogc)); ; fi++) {
             if (fi.frameIndex() == i)
                 return callobj.aliasedVar(fi);
         }
     }
     return v;
 }
 
 inline void
 ArgumentsObject::setElement(uint32_t i, const Value &v)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(!isElementDeleted(i));
     HeapValue &lhs = data()->args[i];
     if (lhs.isMagic(JS_FORWARD_TO_CALL_OBJECT)) {
         CallObject &callobj = getFixedSlot(MAYBE_CALL_SLOT).toObject().asCall();
-        for (AliasedFormalIter fi(callobj.callee().script()); ; fi++) {
+        for (AliasedFormalIter fi(callobj.callee().script().get(nogc)); ; fi++) {
             if (fi.frameIndex() == i) {
                 callobj.setAliasedVar(fi, v);
                 return;
             }
         }
     }
     lhs = v;
 }
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -44,17 +44,17 @@ CopyStackFrameArguments(const StackFrame
             (dst++)->init(*src++);
     }
 }
 
 /* static */ void
 ArgumentsObject::MaybeForwardToCallObject(StackFrame *fp, JSObject *obj, ArgumentsData *data)
 {
     AutoAssertNoGC nogc;
-    RawScript script = fp->script();
+    RawScript script = fp->script().get(nogc);
     if (fp->fun()->isHeavyweight() && script->argsObjAliasesFormals()) {
         obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(fp->callObj()));
         for (AliasedFormalIter fi(script); fi; fi++)
             data->args[fi.frameIndex()] = MagicValue(JS_FORWARD_TO_CALL_OBJECT);
     }
 }
 
 struct CopyStackFrameArgs
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1190,17 +1190,17 @@ Debugger::onSingleStep(JSContext *cx, Va
      * anyway, to make sure the count has the correct non-zero value.
      *
      * The converse --- ensuring that we do receive traps when we should --- can
      * be done with unit tests.
      */
     {
         AutoAssertNoGC nogc;
         uint32_t stepperCount = 0;
-        JSScript *trappingScript = fp->script();
+        JSScript *trappingScript = fp->script().get(nogc);
         GlobalObject *global = &fp->global();
         if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
             for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
                 Debugger *dbg = *p;
                 for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
                     StackFrame *frame = r.front().key;
                     JSObject *frameobj = r.front().value;
                     if (frame->script() == trappingScript &&
@@ -3457,17 +3457,17 @@ DebuggerFrame_getScript(JSContext *cx, u
     return true;
 }
 
 static JSBool
 DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "get offset", args, thisobj, fp);
     AutoAssertNoGC nogc;
-    RawScript script = fp->script();
+    RawScript script = fp->script().get(nogc);
     jsbytecode *pc = fp->pcQuadratic(cx);
     JS_ASSERT(script->code <= pc);
     JS_ASSERT(pc < script->code + script->length);
     size_t offset = pc - script->code;
     args.rval().setNumber(double(offset));
     return true;
 }
 
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -354,22 +354,23 @@ SPSProfiler::registerICCode(mjit::JITChu
     JS_ASSERT(ptr);
     return ptr->value->ics.append(ICInfo(base, size, pc));
 }
 
 void
 SPSProfiler::discardMJITCode(mjit::JITScript *jscr,
                              mjit::JITChunk *chunk, void* address)
 {
+    AutoAssertNoGC nogc;
     if (!jminfo.initialized())
         return;
 
     unregisterScript(jscr->script, chunk);
     for (unsigned i = 0; i < chunk->nInlineFrames; i++)
-        unregisterScript(chunk->inlineFrames()[i].fun->script(), chunk);
+        unregisterScript(chunk->inlineFrames()[i].fun->script().get(nogc), chunk);
 }
 
 void
 SPSProfiler::unregisterScript(JSScript *script, mjit::JITChunk *chunk)
 {
     JITInfoMap::Ptr ptr = jminfo.lookup(script);
     if (!ptr)
         return;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -81,17 +81,17 @@ StaticScopeIter::block() const
     return obj->asStaticBlock();
 }
 
 JSScript *
 StaticScopeIter::funScript() const
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(type() == FUNCTION);
-    return obj->toFunction()->script();
+    return obj->toFunction()->script().get(nogc);
 }
 
 /*****************************************************************************/
 
 StaticScopeIter
 js::ScopeCoordinateToStaticScope(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(pc >= script->code && pc < script->code + script->length);
@@ -1203,17 +1203,17 @@ class DebugScopeProxy : public BaseProxy
                 return false;
 
             AutoAssertNoGC nogc;
             unsigned i = shape->shortid();
             if (block.staticBlock().isAliased(i))
                 return false;
 
             if (maybefp) {
-                RawScript script = maybefp->script();
+                RawScript script = maybefp->script().get(nogc);
                 unsigned local = block.slotToLocalIndex(script->bindings, shape->slot());
                 if (action == GET)
                     *vp = maybefp->unaliasedLocal(local);
                 else
                     maybefp->unaliasedLocal(local) = *vp;
                 JS_ASSERT(analyze::LocalSlot(script, local) >= analyze::TotalSlots(script));
             } else {
                 if (action == GET)
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -133,17 +133,17 @@ inline void
 StackFrame::initCallFrame(JSContext *cx, JSFunction &callee,
                           JSScript *script, uint32_t nactual, StackFrame::Flags flagsArg)
 {
     AutoAssertNoGC nogc;
     JS_ASSERT((flagsArg & ~(CONSTRUCTING |
                             LOWERED_CALL_APPLY |
                             OVERFLOW_ARGS |
                             UNDERFLOW_ARGS)) == 0);
-    JS_ASSERT(script == callee.script());
+    JS_ASSERT(callee.script() == script);
 
     /* Initialize stack frame members. */
     flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
     exec.fun = &callee;
     u.nactual = nactual;
     scopeChain_ = callee.environment();
     ncode_ = NULL;
     initPrev(cx);
@@ -558,26 +558,26 @@ ContextStack::currentScript(jsbytecode *
 #endif
 
 #ifdef JS_METHODJIT
     mjit::CallSite *inlined = regs.inlined();
     if (inlined) {
         mjit::JITChunk *chunk = fp->jit()->chunk(regs.pc);
         JS_ASSERT(inlined->inlineIndex < chunk->nInlineFrames);
         mjit::InlineFrame *frame = &chunk->inlineFrames()[inlined->inlineIndex];
-        RawScript script = frame->fun->script();
+        RawScript script = frame->fun->script().get(nogc);
         if (!allowCrossCompartment && script->compartment() != cx_->compartment)
             return NULL;
         if (ppc)
             *ppc = script->code + inlined->pcOffset;
         return script;
     }
 #endif
 
-    RawScript script = fp->script();
+    RawScript script = fp->script().get(nogc);
     if (!allowCrossCompartment && script->compartment() != cx_->compartment)
         return NULL;
 
     if (ppc)
         *ppc = fp->pcQuadratic(*this);
     return script;
 }
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1192,17 +1192,17 @@ class FrameRegs
         pc = script->code;
         sp = fp.slots() + script->nfixed;
         fp_ = &fp;
         inlined_ = NULL;
     }
 
     void setToEndOfScript() {
         AutoAssertNoGC nogc;
-        RawScript script = fp()->script();
+        RawScript script = fp()->script().get(nogc);
         sp = fp()->base();
         pc = script->code + script->length - JSOP_STOP_LENGTH;
         JS_ASSERT(*pc == JSOP_STOP);
     }
 
     /* For expandInlineFrames: */
     void expandInline(StackFrame *innerfp, jsbytecode *innerpc) {
         pc = innerpc;