Bug 793577 - Implement Return<T> for direct returns of unrooted GC pointers; r=billm r=njn
authorTerrence Cole <terrence@mozilla.com>
Wed, 26 Sep 2012 11:13:20 -0700
changeset 110550 741fb7f8e5cb52247b69fb17b7214ede8c83d8eb
parent 110549 71eacb57041d465e7a32ce2bb2f554ef17b11d63
child 110551 0d27abf6448275d4ebada86d12934bffcc9013c9
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbillm, njn
bugs793577
milestone19.0a1
Bug 793577 - Implement Return<T> for direct returns of unrooted GC pointers; r=billm r=njn Return<T> wraps GC things that are returned from accessor methods. The wrapper helps to ensure correct rooting of the returned pointer and safe access while unrooted.
js/public/Utility.h
js/src/ds/LifoAlloc.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/gc/Root.h
js/src/ion/Bailouts.cpp
js/src/ion/CodeGenerator.cpp
js/src/ion/Ion.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCaches.cpp
js/src/ion/IonFrames-inl.h
js/src/ion/IonFrames.cpp
js/src/ion/IonLinker.h
js/src/ion/TypeOracle.cpp
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/ion/shared/CodeGenerator-shared.h
js/src/ion/x86/Trampoline-x86.cpp
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jsxml.cpp
js/src/methodjit/BaseAssembler.h
js/src/methodjit/BaseCompiler.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/vm/ArgumentsObject-inl.h
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GlobalObject.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/vm/String-inl.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -98,17 +98,17 @@ static JS_ALWAYS_INLINE void
 PrintBacktrace()
 {
     void* OOM_trace[JS_OOM_BACKTRACE_SIZE];
     char** OOM_traceSymbols = NULL;
     int32_t OOM_traceSize = 0;
     int32_t OOM_traceIdx = 0;
     OOM_traceSize = backtrace(OOM_trace, JS_OOM_BACKTRACE_SIZE);
     OOM_traceSymbols = backtrace_symbols(OOM_trace, OOM_traceSize);
-    
+
     if (!OOM_traceSymbols)
         return;
 
     for (OOM_traceIdx = 0; OOM_traceIdx < OOM_traceSize; ++OOM_traceIdx) {
         fprintf(stderr, "#%d %s\n", OOM_traceIdx, OOM_traceSymbols[OOM_traceIdx]);
     }
 
     free(OOM_traceSymbols);
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -16,17 +16,17 @@ BumpChunk *
 BumpChunk::new_(size_t chunkSize)
 {
     JS_ASSERT(RoundUpPow2(chunkSize) == chunkSize);
     void *mem = js_malloc(chunkSize);
     if (!mem)
         return NULL;
     BumpChunk *result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk));
 
-    /* 
+    /*
      * We assume that the alignment of sAlign is less than that of
      * the underlying memory allocator -- creating a new BumpChunk should
      * always satisfy the sAlign alignment constraint.
      */
     JS_ASSERT(AlignPtr(result->bump) == result->bump);
     return result;
 }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2663,17 +2663,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());
+    JS_ASSERT(!fun->script().unsafeGet());
     fun->setScript(bce->script);
     if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
         return false;
 
     bce->tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
@@ -4830,19 +4830,20 @@ EmitFor(JSContext *cx, BytecodeEmitter *
     return pn->pn_left->isKind(PNK_FORIN)
            ? EmitForIn(cx, bce, pn, top)
            : EmitNormalFor(cx, bce, pn, top);
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
+    AssertCanGC();
     RootedFunction fun(cx, pn->pn_funbox->function());
     JS_ASSERT(fun->isInterpreted());
-    if (fun->script()) {
+    if (fun->script().unsafeGet()) {
         /*
          * 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/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -74,16 +74,20 @@ template <typename T>
 class MutableHandleBase {};
 
 } /* namespace js */
 
 namespace JS {
 
 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:
  *   foo(Handle<JSObject*> h);
  * callers can simply write:
  *   foo(NullPtr());
  * which avoids creating a Rooted<JSObject*> just to pass NULL.
  */
 struct NullPtr
@@ -311,16 +315,178 @@ class InternalHandle<T*>
      */
     InternalHandle(T *field)
       : holder(reinterpret_cast<void * const *>(&NullPtr::constNullValue)),
         offset(uintptr_t(field))
     {
     }
 };
 
+#ifdef DEBUG
+template <typename T>
+class IntermediateNoGC
+{
+    T t_;
+
+  public:
+    IntermediateNoGC(const T &t) : t_(t) {
+        EnterAssertNoGCScope();
+    }
+    IntermediateNoGC(const IntermediateNoGC &) {
+        EnterAssertNoGCScope();
+    }
+    ~IntermediateNoGC() {
+        LeaveAssertNoGCScope();
+    }
+
+    const T &operator->() { return t_; }
+    operator const T &() { return t_; }
+};
+#endif
+
+/*
+ * Return<T> wraps GC things that are returned from accessor methods.  The
+ * wrapper helps to ensure correct rooting of the returned pointer and safe
+ * access while unrooted.
+ *
+ * Example usage in a method declaration:
+ *
+ *     class Foo {
+ *         HeapPtrScript script_;
+ *         ...
+ *       public:
+ *          Return<JSScript*> script() { return script_; }
+ *     };
+ *
+ * Example usage of method (1):
+ *
+ *     Foo foo(...);
+ *     RootedScript script(cx, foo->script());
+ *
+ * Example usage of method (2):
+ *
+ *     Foo foo(...);
+ *     foo->script()->needsArgsObj();
+ *
+ * The purpose of this class is to assert eagerly on incorrect use of GC thing
+ * pointers. For example:
+ *
+ *    RootedShape shape(cx, ...);
+ *    shape->parent.init(js_NewGCThing<Shape*>(cx, ...));
+ *
+ * In this expression, C++ is allowed to order these calls as follows:
+ *
+ *   Call                           Effect
+ *   ----                           ------
+ *   1) RootedShape::operator->     Stores shape::ptr_ to stack.
+ *   2) js_NewGCThing<Shape*>       Triggers GC and compaction of shapes. This
+ *                                  moves shape::ptr_ to a new location.
+ *   3) HeapPtrObject::init         This call takes the relocated shape::ptr_
+ *                                  as |this|, crashing or, worse, corrupting
+ *                                  the program's state on the first access
+ *                                  to a member variable.
+ *
+ * If Shape::parent were an accessor function returning a Return<Shape*>, this
+ * could not happen: Return ensures either immediate rooting or no GC within
+ * the same expression.
+ */
+template <typename T>
+class Return
+{
+    friend class Rooted<T>;
+
+    const T ptr_;
+
+  public:
+    template <typename S>
+    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.
+     *
+     * Example:
+     *     AutoAssertNoGC nogc;
+     *     RawScript script = fun->script();
+     */
+    operator const T &() const {
+        JS_ASSERT(InNoGCScope());
+        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
+     * encountered before the next C++ Sequence Point.
+     *
+     * INCORRECT:
+     *    fun->script()->bindings = myBindings->clone(cx, ...);
+     *
+     * The compiler is allowed to reorder |fun->script()::operator->()| above
+     * the call to |clone(cx, ...)|. In this case, the RawScript C++ stores on
+     * the stack may be corrupted by a GC under |clone|. The subsequent
+     * dereference of this pointer to get |bindings| will result in an invalid
+     * access. This wrapper ensures that such usage asserts in DEBUG builds when
+     * it encounters this situation. Without this assertion, it is possible for
+     * such access to corrupt program state instead of crashing immediately.
+     *
+     * CORRECT:
+     *    RootedScript clone(cx, myBindings->clone(cx, ...));
+     *    fun->script()->bindings = clone;
+     */
+#ifdef DEBUG
+    IntermediateNoGC<T> operator->() const {
+        return IntermediateNoGC<T>(ptr_);
+    }
+#else
+    const T &operator->() const {
+        return ptr_;
+    }
+#endif
+
+    /*
+     * |unsafeGet()| is unsafe for most uses.  Although it performs similar
+     * checking to |operator->|, its result can be stored to a local variable.
+     * For this reason, it should only be used when it would be incorrect or
+     * absurd to create a new Rooted for its use: e.g. for assertions.
+     */
+#ifdef DEBUG
+    IntermediateNoGC<T> unsafeGet() const {
+        return IntermediateNoGC<T>(ptr_);
+    }
+#else
+    const T &unsafeGet() const {
+        return ptr_;
+    }
+#endif
+
+    /*
+     * |operator==| is safe to use in any context.  It is present to allow:
+     *     JS_ASSERT(myScript == fun->script().unsafeGet());
+     *
+     * 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 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
  * Rooted<T> may be used without the class definition being available.
  */
 template <typename T>
 struct RootKind<T *> { static ThingRootKind rootKind() { return T::rootKind(); } };
 
@@ -402,16 +568,25 @@ class Rooted : public RootedBase<T>
     Rooted(JSContext *cx, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
+    template <typename S>
+    Rooted(JSContext *cx, const Return<S> &initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial.ptr_)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        init(cx);
+    }
+
     ~Rooted()
     {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         JS_ASSERT(*stack == this);
         *stack = prev;
 #endif
     }
 
@@ -434,26 +609,40 @@ class Rooted : public RootedBase<T>
     }
 
     T & operator =(const Rooted &value)
     {
         ptr = value;
         return ptr;
     }
 
+    template <typename S>
+    T & operator =(const Return<S> &value)
+    {
+        ptr = value.ptr_;
+        return ptr;
+    }
+
   private:
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
     Rooted<T> **stack, *prev;
 #endif
     T ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     Rooted(const Rooted &) MOZ_DELETE;
 };
 
+template <typename T>
+bool
+Return<T>::operator==(const Rooted<T> &other)
+{
+    return ptr_ == other.get();
+}
+
 typedef Rooted<JSObject*>    RootedObject;
 typedef Rooted<JSFunction*>  RootedFunction;
 typedef Rooted<JSScript*>    RootedScript;
 typedef Rooted<JSString*>    RootedString;
 typedef Rooted<jsid>         RootedId;
 typedef Rooted<Value>        RootedValue;
 
 /*
@@ -539,20 +728,16 @@ Handle<T>::Handle(MutableHandle<S> &root
 template<typename T> template <typename S>
 inline
 MutableHandle<T>::MutableHandle(js::Rooted<S> *root,
                                 typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
 {
     ptr = root->address();
 }
 
-JS_FRIEND_API(void) EnterAssertNoGCScope();
-JS_FRIEND_API(void) LeaveAssertNoGCScope();
-JS_FRIEND_API(bool) InNoGCScope();
-
 /*
  * The scoped guard object AutoAssertNoGC forces the GC to assert if a GC is
  * attempted while the guard object is live.  If you have a GC-unsafe operation
  * to perform, use this guard object to protect your operation.
  */
 class AutoAssertNoGC
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -72,16 +72,18 @@ IonBailoutIterator::dump() const
     } else {
         IonFrameIterator::dump();
     }
 }
 
 static JSScript*
 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();
       }
@@ -91,16 +93,17 @@ GetBailedJSScript(JSContext *cx)
         JS_NOT_REACHED("unexpected callee token kind");
         return NULL;
     }
 }
 
 void
 StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
 {
+    AutoAssertNoGC nogc;
     uint32 exprStackSlots = iter.slots() - script()->nfixed;
 
 #ifdef TRACK_SNAPSHOTS
     iter.spewBailingFrom();
 #endif
     IonSpew(IonSpew_Bailouts, " expr stack slots %u, is function frame %u",
             exprStackSlots, isFunctionFrame());
 
@@ -177,52 +180,55 @@ StackFrame::initFromBailout(JSContext *c
     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));
 }
 
 static StackFrame *
 PushInlinedFrame(JSContext *cx, StackFrame *callerFrame)
 {
+    AssertCanGC();
+
     // Grab the callee object out of the caller's frame, which has already been restored.
     // N.B. we currently assume that the caller frame is at a JSOP_CALL pc for the caller frames,
     // which will not be the case when we inline getters (in which case it would be a
     // JSOP_GETPROP). That will have to be handled differently.
     FrameRegs &regs = cx->regs();
     JS_ASSERT(JSOp(*regs.pc) == JSOP_CALL || JSOp(*regs.pc) == JSOP_NEW);
     int callerArgc = GET_ARGC(regs.pc);
     const Value &calleeVal = regs.sp[-callerArgc - 2];
 
-    JSFunction *fun = calleeVal.toObject().toFunction();
-    JSScript *script = fun->script();
+    RootedFunction fun(cx, calleeVal.toObject().toFunction());
+    RootedScript script(cx, fun->script());
     CallArgs inlineArgs = CallArgsFromSp(callerArgc, regs.sp);
-    
+
     // Bump the stack pointer to make it look like the inline args have been pushed, but they will
     // really get filled in by RestoreOneFrame.
     regs.sp = inlineArgs.end();
 
     InitialFrameFlags flags = INITIAL_NONE;
     if (JSOp(*regs.pc) == JSOP_NEW)
         flags = INITIAL_CONSTRUCT;
 
     if (!cx->stack.pushInlineFrame(cx, regs, inlineArgs, *fun, script, flags, DONT_REPORT_ERROR))
         return NULL;
 
     StackFrame *fp = cx->stack.fp();
     JS_ASSERT(fp == regs.fp());
     JS_ASSERT(fp->prev() == callerFrame);
-    
+
     fp->formals()[-2].setObject(*fun);
 
     return fp;
 }
 
 static uint32
 ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it)
 {
+    AssertCanGC();
     IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p",
             it.script()->filename, it.script()->lineno, (void *) it.ionScript());
     IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u",
             it.snapshotOffset(), it.ionScript()->snapshotsSize());
 #ifdef DEBUG
     // Use count is reset after invalidation. Log use count on bailouts to
     // determine if we have a critical sequence of bailout.
     if (it.script()->ion == it.ionScript()) {
@@ -248,17 +254,17 @@ ConvertFrames(JSContext *cx, IonActivati
 
     StackFrame *fp;
     if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) {
         // Avoid creating duplicate interpreter frames. This is necessary to
         // avoid blowing out the interpreter stack, and must be used in
         // conjunction with inline-OSR from within bailouts (since each Ion
         // activation must be tied to a unique JSStackFrame for StackIter to
         // work).
-        // 
+        //
         // Note: If the entry frame is a placeholder (a stub frame pushed for
         // JM -> Ion calls), then we cannot re-use it as it does not have
         // enough slots.
         JS_ASSERT(cx->fp() == activation->entryfp());
         fp = cx->fp();
         cx->regs().sp = fp->base();
     } else {
         br->constructFrame();
@@ -313,17 +319,17 @@ ConvertFrames(JSContext *cx, IonActivati
         return BAILOUT_RETURN_CACHED_SHAPE_GUARD;
 
       // When bailing out from an argument check, none of the code of the
       // function has run yet. When profiling, this means that the function
       // hasn't flagged its entry just yet. It has been "entered," however, so
       // we flag it here manually that the entry has happened.
       case Bailout_ArgumentCheck:
         fp->unsetPushedSPSFrame();
-        Probes::enterScript(cx, fp->script(), fp->script()->function(), fp);
+        Probes::enterScript(cx, fp->script().unsafeGet(), fp->script()->function(), fp);
         return BAILOUT_RETURN_ARGUMENT_CHECK;
     }
 
     JS_NOT_REACHED("bad bailout kind");
     return BAILOUT_RETURN_FATAL_ERROR;
 }
 
 static inline void
@@ -346,16 +352,17 @@ EnsureExitFrame(IonCommonFrameLayout *fr
 
     JS_ASSERT(frame->prevType() == IonFrame_JS);
     frame->changePrevType(IonFrame_Bailed_JS);
 }
 
 uint32
 ion::Bailout(BailoutStack *sp)
 {
+    AssertCanGC();
     JSContext *cx = GetIonContext()->cx;
     // We don't have an exit frame.
     cx->runtime->ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
     IonActivation *activation = ionActivations.activation();
 
     // IonCompartment *ioncompartment = cx->compartment->ionCompartment();
@@ -368,16 +375,17 @@ ion::Bailout(BailoutStack *sp)
 
     EnsureExitFrame(iter.jsFrame());
     return retval;
 }
 
 uint32
 ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut)
 {
+    AssertCanGC();
     sp->checkInvariants();
 
     JSContext *cx = GetIonContext()->cx;
 
     // We don't have an exit frame.
     cx->runtime->ionTop = NULL;
     IonActivationIterator ionActivations(cx);
     IonBailoutIterator iter(ionActivations, sp);
@@ -490,17 +498,17 @@ ion::ReflowTypeInfo(uint32 bailoutResult
 
     return true;
 }
 
 uint32
 ion::RecompileForInlining()
 {
     JSContext *cx = GetIonContext()->cx;
-    JSScript *script = cx->fp()->script();
+    RawScript script = cx->fp()->script().unsafeGet();
 
     IonSpew(IonSpew_Inlining, "Recompiling script to inline calls %s:%d", script->filename,
             script->lineno);
 
     // Invalidate the script to force a recompile.
     if (!Invalidate(cx, script, /* resetUses */ false))
         return BAILOUT_RETURN_FATAL_ERROR;
 
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -303,17 +303,18 @@ CodeGenerator::visitLambda(LLambda *lir)
         } s;
         uint32_t word;
     } u;
     u.s.nargs = fun->nargs;
     u.s.flags = fun->flags & ~JSFUN_EXTENDED;
 
     JS_STATIC_ASSERT(offsetof(JSFunction, flags) == offsetof(JSFunction, nargs) + 2);
     masm.store32(Imm32(u.word), Address(output, offsetof(JSFunction, nargs)));
-    masm.storePtr(ImmGCPtr(fun->script()), Address(output, JSFunction::offsetOfNativeOrScript()));
+    masm.storePtr(ImmGCPtr(fun->script().unsafeGet()),
+                  Address(output, JSFunction::offsetOfNativeOrScript()));
     masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
@@ -2895,16 +2896,17 @@ CodeGenerator::visitGetArgument(LGetArgu
         masm.loadValue(argPtr, result);
     }
     return true;
 }
 
 bool
 CodeGenerator::generate()
 {
+    AssertCanGC();
     JSContext *cx = GetIonContext()->cx;
 
     unsigned slots = graph.localSlotCount() +
                      (graph.argumentSlotCount() * sizeof(Value) / STACK_SLOT_SIZE);
     if (!safepoints_.init(slots))
         return false;
 
     // Before generating any code, we generate type checks for all parameters.
@@ -2936,17 +2938,17 @@ CodeGenerator::generate()
     Linker linker(masm);
     IonCode *code = linker.newCode(cx);
     if (!code)
         return false;
 
     // We encode safepoints after the OSI-point offsets have been determined.
     encodeSafepoints();
 
-    JSScript *script = gen->info().script();
+    RootedScript script(cx, gen->info().script());
     JS_ASSERT(!script->hasIonScript());
 
     uint32 scriptFrameSize = frameClass_ == FrameSizeClass::None()
                            ? frameDepth_
                            : FrameSizeClass::FromDepth(frameDepth_).frameSize();
 
     script->ion = IonScript::New(cx, slots, scriptFrameSize, snapshots_.size(),
                                  bailouts_.length(), graph.numConstants(),
@@ -4204,16 +4206,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
 
     JS_ASSERT(masm.framePushed() == initialStack);
     return true;
 }
 
 bool
 CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
 {
+    AssertCanGC();
     Register temp = ToRegister(lir->temp()->output());
 
     switch (lir->type()) {
         case MFunctionBoundary::Inline_Enter:
             // Multiple scripts can be inlined at one depth, but there is only
             // one Inline_Exit node to signify this. To deal with this, if we
             // reach the entry of another inline script on the same level, then
             // just reset the sps metadata about the frame. We must balance
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -285,16 +285,18 @@ IonActivation::~IonActivation()
     cx_->runtime->ionActivation = prev();
     cx_->runtime->ionTop = prevIonTop_;
     cx_->runtime->ionJSContext = prevIonJSContext_;
 }
 
 IonCode *
 IonCode::New(JSContext *cx, uint8 *code, uint32 bufferSize, JSC::ExecutablePool *pool)
 {
+    AssertCanGC();
+
     IonCode *codeObj = gc::NewGCThing<IonCode>(cx, gc::FINALIZE_IONCODE, sizeof(IonCode));
     if (!codeObj) {
         pool->release();
         return NULL;
     }
 
     new (codeObj) IonCode(code, bufferSize, pool);
     return codeObj;
@@ -904,32 +906,33 @@ class AutoDestroyAllocator
         if (alloc)
             js_delete(alloc);
     }
 };
 
 void
 AttachFinishedCompilations(JSContext *cx)
 {
+    AssertCanGC();
     IonCompartment *ion = cx->compartment->ionCompartment();
     if (!ion)
         return;
 
     AutoLockWorkerThreadState lock(cx->runtime);
 
     OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
     // Incorporate any off thread compilations which have finished, failed or
     // have been cancelled, and destroy JM jitcode for any compilations which
     // succeeded, to allow entering the Ion code from the interpreter.
     while (!compilations.empty()) {
         IonBuilder *builder = compilations.popCopy();
 
         if (builder->lir) {
-            JSScript *script = builder->script();
+            RootedScript script(cx, builder->script());
             IonContext ictx(cx, cx->compartment, &builder->temp());
 
             CodeGenerator codegen(builder, *builder->lir);
 
             types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
             enterCompiler.initExisting(builder->recompileInfo);
 
             bool success;
@@ -956,16 +959,17 @@ AttachFinishedCompilations(JSContext *cx
     compilations.clear();
 }
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 static bool
 IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
 {
+    AssertCanGC();
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
 #endif
 
     LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
@@ -999,17 +1003,17 @@ IonCompile(JSContext *cx, JSScript *scri
     types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
     enterCompiler.init(script, false, 0);
 
     AutoTempAllocatorRooter root(cx, temp);
 
     IonBuilder *builder = alloc->new_<IonBuilder>(cx, temp, graph, &oracle, info);
     JS_ASSERT(!builder->script()->ion);
 
-    IonSpewNewFunction(graph, builder->script());
+    IonSpewNewFunction(graph, builder->script().unsafeGet());
 
     if (!builder->build()) {
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return false;
     }
     builder->clearForBackEnd();
 
     if (js_IonOptions.parallelCompilation) {
@@ -1289,28 +1293,30 @@ ion::CanEnterUsingFastInvoke(JSContext *
     // Skip if the code is expected to result in a bailout.
     if (!script->hasIonScript() || script->ion->bailoutExpected())
         return Method_Skipped;
 
     if (!cx->compartment->ensureIonCompartmentExists(cx))
         return Method_Error;
 
     // This can GC, so afterward, script->ion is not guaranteed to be valid.
+    AssertCanGC();
     if (!cx->compartment->ionCompartment()->enterJIT(cx))
         return Method_Error;
 
     if (!script->ion)
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
 static IonExecStatus
 EnterIon(JSContext *cx, StackFrame *fp, void *jitcode)
 {
+    AssertCanGC();
     JS_CHECK_RECURSION(cx, return IonExec_Error);
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT(CheckFrame(fp));
     JS_ASSERT(!fp->script()->ion->bailoutExpected());
 
     EnterIonCode enter = cx->compartment->ionCompartment()->enterJITInfallible();
 
     // maxArgc is the maximum of arguments between the number of actual
@@ -1340,17 +1346,17 @@ EnterIon(JSContext *cx, StackFrame *fp, 
 
             // The beginning of the actual args is not updated, so we just copy
             // the formal args into the actual args to get a linear vector which
             // can be copied by generateEnterJit.
             memcpy(maxArgv, formalArgv, formalArgc * sizeof(Value));
         }
         calleeToken = CalleeToToken(&fp->callee());
     } else {
-        calleeToken = CalleeToToken(fp->script());
+        calleeToken = CalleeToToken(fp->script().unsafeGet());
     }
 
     // Caller must construct |this| before invoking the Ion function.
     JS_ASSERT_IF(fp->isConstructing(), fp->functionThis().isObject());
 
     Value result = Int32Value(numActualArgs);
     {
         AssertCompartmentUnchanged pcc(cx);
@@ -1380,17 +1386,18 @@ EnterIon(JSContext *cx, StackFrame *fp, 
 
     JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
     return result.isMagic() ? IonExec_Error : IonExec_Ok;
 }
 
 IonExecStatus
 ion::Cannon(JSContext *cx, StackFrame *fp)
 {
-    JSScript *script = fp->script();
+    AssertCanGC();
+    RootedScript script(cx, fp->script());
     IonScript *ion = script->ion;
     IonCode *code = ion->method();
     void *jitcode = code->raw();
 
 #if JS_TRACE_LOGGING
     TraceLog(TraceLogging::defaultLogger(),
              TraceLogging::ION_CANNON_START,
              script);
@@ -1411,17 +1418,18 @@ ion::Cannon(JSContext *cx, StackFrame *f
 #endif
 
     return status;
 }
 
 IonExecStatus
 ion::SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc)
 {
-    JSScript *script = fp->script();
+    AssertCanGC();
+    RootedScript script(cx, fp->script());
     IonScript *ion = script->ion;
     IonCode *code = ion->method();
     void *osrcode = code->raw() + ion->osrEntryOffset();
 
     JS_ASSERT(ion->osrPc() == pc);
 
 #if JS_TRACE_LOGGING
     TraceLog(TraceLogging::defaultLogger(),
@@ -1446,17 +1454,17 @@ ion::SideCannon(JSContext *cx, StackFram
     return status;
 }
 
 IonExecStatus
 ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args)
 {
     JS_CHECK_RECURSION(cx, return IonExec_Error);
 
-    HandleScript script = fun->script();
+    RootedScript script(cx, fun->script());
     IonScript *ion = script->ionScript();
     IonCode *code = ion->method();
     void *jitcode = code->raw();
 
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT(!script->ion->bailoutExpected());
 
     bool clearCallingIntoIon = false;
@@ -1501,16 +1509,17 @@ ion::FastInvoke(JSContext *cx, HandleFun
 
     JS_ASSERT_IF(result.isMagic(), result.isMagic(JS_ION_ERROR));
     return result.isMagic() ? IonExec_Error : IonExec_Ok;
 }
 
 static void
 InvalidateActivation(FreeOp *fop, uint8 *ionTop, bool invalidateAll)
 {
+    AutoAssertNoGC nogc;
     IonSpew(IonSpew_Invalidate, "BEGIN invalidating activation");
 
     size_t frameno = 1;
 
     for (IonFrameIterator it(ionTop); !it.done(); ++it, ++frameno) {
         JS_ASSERT_IF(frameno == 1, it.type() == IonFrame_Exit);
 
 #ifdef DEBUG
@@ -1546,17 +1555,17 @@ InvalidateActivation(FreeOp *fop, uint8 
 
         if (!it.isScripted())
             continue;
 
         // See if the frame has already been invalidated.
         if (it.checkInvalidation())
             continue;
 
-        JSScript *script = it.script();
+        RawScript script = it.script();
         if (!script->hasIonScript())
             continue;
 
         if (!invalidateAll && !script->ion->invalidated())
             continue;
 
         IonScript *ionScript = script->ion;
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -141,21 +141,23 @@ IonBuilder::CFGState::LookupSwitch(jsbyt
         (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(sizeof(FixedList<MBasicBlock *>));
     state.lookupswitch.currentBlock = 0;
     return state;
 }
 
 JSFunction *
 IonBuilder::getSingleCallTarget(uint32 argc, jsbytecode *pc)
 {
+    AutoAssertNoGC nogc;
+
     types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
     if (!calleeTypes)
         return NULL;
 
-    JSObject *obj = calleeTypes->getSingleton();
+    RawObject obj = calleeTypes->getSingleton();
     if (!obj || !obj->isFunction())
         return NULL;
 
     return obj->toFunction();
 }
 
 uint32_t
 IonBuilder::getPolyCallTargets(uint32 argc, jsbytecode *pc,
@@ -181,27 +183,29 @@ IonBuilder::getPolyCallTargets(uint32 ar
     }
 
     return (uint32_t) objCount;
 }
 
 bool
 IonBuilder::canInlineTarget(JSFunction *target)
 {
+    AssertCanGC();
+
     if (!target->isInterpreted()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
         return false;
     }
 
     if (target->getParent() != &script_->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
         return false;
     }
 
-    JSScript *inlineScript = target->script();
+    RootedScript inlineScript(cx, target->script());
 
     if (!inlineScript->canIonCompile()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to disable Ion compilation");
         return false;
     }
 
     // Allow inlining of recursive calls, but only one level deep.
     IonBuilder *builder = callerBuilder_;
@@ -751,16 +755,18 @@ IonBuilder::markPhiBytecodeUses(jsbyteco
         if (def->isPhi())
             def->toPhi()->setHasBytecodeUses();
     }
 }
 
 bool
 IonBuilder::inspectOpcode(JSOp op)
 {
+    AssertCanGC();
+
     // Don't compile fat opcodes, run the decomposed version instead.
     if (js_CodeSpec[op].format & JOF_DECOMPOSE)
         return true;
 
     switch (op) {
       case JSOP_LOOPENTRY:
         insertRecompileCheck();
         return true;
@@ -2792,16 +2798,18 @@ class AutoAccumulateExits
 };
 
 
 bool
 IonBuilder::jsop_call_inline(HandleFunction callee, uint32 argc, bool constructing,
                              MConstant *constFun, MBasicBlock *bottom,
                              Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns)
 {
+    AssertCanGC();
+
     // Rewrite the stack position containing the function with the constant
     // function definition, before we take the inlineResumePoint
     current->rewriteAtDepth(-((int) argc + 2), constFun);
 
     // This resume point collects outer variables only.  It is used to recover
     // the stack state before the current bytecode.
     MResumePoint *inlineResumePoint =
         MResumePoint::New(current, pc, callerResumePoint_, MResumePoint::Outer);
@@ -2817,26 +2825,27 @@ IonBuilder::jsop_call_inline(HandleFunct
     MDefinitionVector argv;
     if (!argv.resizeUninitialized(argc + 1))
         return false;
     for (int32 i = argc; i >= 0; i--)
         argv[i] = current->pop();
 
     // Compilation information is allocated for the duration of the current tempLifoAlloc
     // lifetime.
-    CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(callee->script(), callee,
+    RootedScript calleeScript(cx, callee->script());
+    CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(calleeScript.get(), callee,
                                                               (jsbytecode *)NULL, constructing);
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     TypeInferenceOracle oracle;
-    if (!oracle.init(cx, callee->script()))
+    if (!oracle.init(cx, calleeScript))
         return false;
 
     IonBuilder inlineBuilder(cx, &temp(), &graph(), &oracle,
                              info, inliningDepth + 1, loopDepth_);
 
     // Create |this| on the caller-side for inlined constructors.
     MDefinition *thisDefn = NULL;
     if (constructing) {
@@ -2882,16 +2891,18 @@ IonBuilder::jsop_call_inline(HandleFunct
     }
     JS_ASSERT(!retvalDefns.empty());
     return true;
 }
 
 bool
 IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32 argc)
 {
+    AssertCanGC();
+
     if (inliningDepth >= js_IonOptions.maxInlineDepth)
         return false;
 
     // For "small" functions, we should be more aggressive about inlining.
     // This is based on the following intuition:
     //  1. The call overhead for a small function will likely be a much
     //     higher proportion of the runtime of the function than for larger
     //     functions.
@@ -2901,22 +2912,24 @@ IonBuilder::makeInliningDecision(AutoObj
     //  3. Do not inline functions which are not called as frequently as their
     //     callers.
 
     uint32_t callerUses = script_->getUseCount();
 
     uint32_t totalSize = 0;
     uint32_t checkUses = js_IonOptions.usesBeforeInlining;
     bool allFunctionsAreSmall = true;
+    RootedFunction target(cx);
+    RootedScript script(cx);
     for (size_t i = 0; i < targets.length(); i++) {
-        JSFunction *target = targets[i]->toFunction();
+        target = targets[i]->toFunction();
         if (!target->isInterpreted())
             return false;
 
-        JSScript *script = target->script();
+        script = target->script();
         uint32_t calleeUses = script->getUseCount();
 
         if (target->nargs < argc) {
             IonSpew(IonSpew_Inlining, "Not inlining, overflow of arguments.");
             return false;
         }
 
         totalSize += script->length;
@@ -3525,17 +3538,17 @@ IonBuilder::getSingletonPrototype(JSFunc
 MDefinition *
 IonBuilder::createThisScriptedSingleton(HandleFunction target, HandleObject proto, MDefinition *callee)
 {
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
     types::TypeObject *type = proto->getNewType(cx, target);
     if (!type)
         return NULL;
-    if (!types::TypeScript::ThisTypes(target->script())->hasType(types::Type::ObjectType(type)))
+    if (!types::TypeScript::ThisTypes(target->script().unsafeGet())->hasType(types::Type::ObjectType(type)))
         return NULL;
 
     RootedObject templateObject(cx, js_CreateThisForFunctionWithProto(cx, target, proto));
     if (!templateObject)
         return NULL;
 
     // Trigger recompilation if the templateObject changes.
     if (templateObject->type()->newScript)
@@ -3690,16 +3703,18 @@ IonBuilder::jsop_funapply(uint32 argc)
     types::StackTypeSet *barrier;
     types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
     return pushTypeBarrier(apply, types, barrier);
 }
 
 bool
 IonBuilder::jsop_call(uint32 argc, bool constructing)
 {
+    AssertCanGC();
+
     // Acquire known call target if existent.
     AutoObjectVector targets(cx);
     uint32_t numTargets = getPolyCallTargets(argc, pc, targets, 4);
     types::StackTypeSet *barrier;
     types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
 
     // Attempt to inline native and scripted functions.
     if (inliningEnabled()) {
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -428,17 +428,17 @@ class IonBuilder : public MIRGenerator
     // Compilation index for this attempt.
     types::RecompileInfo const recompileInfo;
 
     // If off thread compilation is successful, final LIR is attached here.
     LIRGraph *lir;
 
     void clearForBackEnd();
 
-    JSScript *script() const { return script_; }
+    Return<JSScript*> script() const { return script_; }
 
   private:
     JSContext *cx;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32 loopDepth_;
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -1308,24 +1308,25 @@ IonCacheBindName::attachGlobal(JSContext
     IonSpew(IonSpew_InlineCaches, "Generated BINDNAME global stub at %p", code->raw());
     return true;
 }
 
 static inline void
 GenerateScopeChainGuard(MacroAssembler &masm, JSObject *scopeObj,
                         Register scopeObjReg, Shape *shape, Label *failures)
 {
+    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()) {
-            JSFunction *fun = &callObj->callee();
-            JSScript *script = fun->script();
+            RawFunction fun = &callObj->callee();
+            RawScript script = fun->script();
             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-inl.h
+++ b/js/src/ion/IonFrames-inl.h
@@ -79,16 +79,17 @@ IonFrameIterator::frameSize() const
     JS_ASSERT(type_ != IonFrame_Exit);
     return frameSize_;
 }
 
 // Returns the JSScript associated with the topmost Ion frame.
 inline JSScript *
 GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
 {
+    AutoAssertNoGC nogc;
     IonFrameIterator iter(cx->runtime->ionTop);
     JS_ASSERT(iter.type() == IonFrame_Exit);
     ++iter;
 
     // If needed, grab the safepoint index.
     if (safepointIndexOut)
         *safepointIndexOut = iter.safepoint();
 
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -24,16 +24,17 @@
 #include "VMFunctions.h"
 
 using namespace js;
 using namespace js::ion;
 
 JSScript *
 ion::MaybeScriptFromCalleeToken(CalleeToken token)
 {
+    AutoAssertNoGC nogc;
     switch (GetCalleeTokenTag(token)) {
       case CalleeToken_Script:
         return CalleeTokenToScript(token);
       case CalleeToken_Function:
         return CalleeTokenToFunction(token)->script();
     }
     JS_NOT_REACHED("invalid callee token tag");
     return NULL;
@@ -62,18 +63,19 @@ IonFrameIterator::checkInvalidation() co
 {
     IonScript *dummy;
     return checkInvalidation(&dummy);
 }
 
 bool
 IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
 {
+    AutoAssertNoGC nogc;
     uint8 *returnAddr = returnAddressToFp();
-    JSScript *script = this->script();
+    RawScript script = this->script();
     // N.B. the current IonScript is not the same as the frame's
     // IonScript if the frame has since been invalidated.
     IonScript *currentIonScript = script->ion;
     bool invalidated = !script->hasIonScript() ||
         !currentIonScript->containsReturnAddress(returnAddr);
     if (!invalidated)
         return false;
 
@@ -165,18 +167,19 @@ IonFrameIterator::isEntryJSFrame() const
             return false;
     }
     return true;
 }
 
 JSScript *
 IonFrameIterator::script() const
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(isScripted());
-    JSScript *script = MaybeScriptFromCalleeToken(calleeToken());
+    RawScript script = MaybeScriptFromCalleeToken(calleeToken());
     JS_ASSERT(script);
     return script;
 }
 
 Value *
 IonFrameIterator::nativeVp() const
 {
     JS_ASSERT(isNative());
@@ -255,16 +258,17 @@ IonFrameIterator::machineState() const
         machine.setRegisterLocation(*iter, --spill);
 
     return machine;
 }
 
 static void
 CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32 localSlot)
 {
+    AssertCanGC();
     SnapshotIterator si = frame.snapshotIterator();
 
     // Skip stack slots until we reach the iterator object.
     uint32 base = CountArgSlots(frame.maybeCallee()) + frame.script()->nfixed;
     uint32 skipSlots = base + localSlot - 1;
 
     for (unsigned i = 0; i < skipSlots; i++)
         si.skip();
@@ -276,17 +280,18 @@ CloseLiveIterator(JSContext *cx, const I
         UnwindIteratorForException(cx, obj);
     else
         UnwindIteratorForUncatchableException(cx, obj);
 }
 
 static void
 CloseLiveIterators(JSContext *cx, const InlineFrameIterator &frame)
 {
-    JSScript *script = frame.script();
+    AssertCanGC();
+    RootedScript script(cx, frame.script());
     jsbytecode *pc = frame.pc();
 
     if (!script->hasTrynotes())
         return;
 
     JSTryNote *tn = script->trynotes()->vector;
     JSTryNote *tnEnd = tn + script->trynotes()->length;
 
@@ -306,16 +311,17 @@ CloseLiveIterators(JSContext *cx, const 
         uint32 localSlot = tn->stackDepth;
         CloseLiveIterator(cx, frame, localSlot);
     }
 }
 
 void
 ion::HandleException(ResumeFromException *rfe)
 {
+    AssertCanGC();
     JSContext *cx = GetIonContext()->cx;
 
     IonSpew(IonSpew_Invalidate, "handling exception");
 
     // Immediately remove any bailout frame guard that might be left over from
     // an error in between ConvertFrames and ThunkToInterpreter.
     js_delete(cx->runtime->ionActivation->maybeTakeBailout());
 
@@ -326,17 +332,18 @@ ion::HandleException(ResumeFromException
             // them.
             InlineFrameIterator frames(&iter);
             for (;;) {
                 CloseLiveIterators(cx, frames);
 
                 // 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();
+                AutoAssertNoGC nogc;
+                RawScript script = frames.script();
                 Probes::exitScript(cx, script, script->function(), NULL);
                 if (!frames.more())
                     break;
                 ++frames;
             }
 
             IonScript *ionScript;
             if (iter.checkInvalidation(&ionScript))
@@ -895,16 +902,17 @@ InlineFrameIterator::InlineFrameIterator
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
 
 void
 InlineFrameIterator::findNextFrame()
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(more());
 
     si_ = start_;
 
     // Read the initial frame.
     callee_ = frame_->maybeCallee();
     script_ = frame_->script();
     pc_ = script_->code + si_.pcOffset();
@@ -1136,16 +1144,17 @@ struct DumpOp {
 #endif
         i_++;
     }
 };
 
 void
 InlineFrameIterator::dump() const
 {
+    AutoAssertNoGC nogc;
     if (more())
         fprintf(stderr, " JS frame (inlined)\n");
     else
         fprintf(stderr, " JS frame\n");
 
     bool isFunction = false;
     if (isFunctionFrame()) {
         isFunction = true;
--- a/js/src/ion/IonLinker.h
+++ b/js/src/ion/IonLinker.h
@@ -24,16 +24,17 @@ class Linker
     MacroAssembler &masm;
 
     IonCode *fail(JSContext *cx) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
     IonCode *newCode(JSContext *cx, IonCompartment *comp) {
+        AssertCanGC();
 #ifndef JS_CPU_ARM
         masm.flush();
 #endif
         if (masm.oom())
             return fail(cx);
 
         JSC::ExecutablePool *pool;
         size_t bytesNeeded = masm.bytesNeeded() + sizeof(IonCode *) + CodeAlignment;
--- a/js/src/ion/TypeOracle.cpp
+++ b/js/src/ion/TypeOracle.cpp
@@ -546,17 +546,18 @@ TypeInferenceOracle::canInlineCall(JSScr
     if (code->monitoredTypes || code->monitoredTypesReturn || caller->analysis()->typeBarriers(cx, pc))
         return false;
     return true;
 }
 
 bool
 TypeInferenceOracle::canEnterInlinedFunction(JSFunction *target)
 {
-    JSScript *script = target->script();
+    AssertCanGC();
+    RootedScript script(cx, target->script());
     if (!script->hasAnalysis() || !script->analysis()->ranInference())
         return false;
 
     if (!script->analysis()->inlineable())
         return false;
 
     if (script->analysis()->usesScopeChain())
         return false;
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -349,16 +349,17 @@ CodeGeneratorShared::markOsiPoint(LOsiPo
     return osiIndices_.append(OsiIndex(*callPointOffset, so));
 }
 
 // Before doing any call to Cpp, you should ensure that volatile
 // registers are evicted by the register allocator.
 bool
 CodeGeneratorShared::callVM(const VMFunction &fun, LInstruction *ins, const Register *dynStack)
 {
+    AssertCanGC();
 #ifdef DEBUG
     if (ins->mirRaw()) {
         JS_ASSERT(ins->mirRaw()->isInstruction());
         MInstruction *mir = ins->mirRaw()->toInstruction();
         JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint());
     }
 #endif
 
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -505,16 +505,17 @@ CodeGeneratorShared::oolCallVM(const VMF
         return NULL;
     return ool;
 }
 
 template <class ArgSeq, class StoreOutputTo>
 bool
 CodeGeneratorShared::visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool)
 {
+    AssertCanGC();
     LInstruction *lir = ool->lir();
 
     saveLive(lir);
     ool->args().generate(this);
     if (!callVM(ool->function(), lir))
         return false;
     ool->out().generate(this);
     restoreLive(lir);
--- a/js/src/ion/x86/Trampoline-x86.cpp
+++ b/js/src/ion/x86/Trampoline-x86.cpp
@@ -381,16 +381,17 @@ IonCompartment::generateBailoutHandler(J
 
     Linker linker(masm);
     return linker.newCode(cx);
 }
 
 IonCode *
 IonCompartment::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
+    AssertCanGC();
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(!StackKeptAligned);
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
         return p->value;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7220,16 +7220,18 @@ JS_IsIdentifier(JSContext *cx, JSString 
 
     *isIdentifier = js::frontend::IsIdentifier(linearStr);
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DescribeScriptedCaller(JSContext *cx, JSScript **script, unsigned *lineno)
 {
+    AutoAssertNoGC nogc;
+
     if (script)
         *script = NULL;
     if (lineno)
         *lineno = 0;
 
     ScriptFrameIter i(cx);
     if (i.done())
         return JS_FALSE;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -556,16 +556,18 @@ AutoResolving::alreadyStartedSlow() cons
 }
 
 } /* namespace js */
 
 static void
 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
             JSErrorCallback callback, void *userRef)
 {
+    AssertCanGC();
+
     /*
      * Check the error report, and set a JavaScript-catchable exception
      * if the error is defined to have an associated exception.  If an
      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
      * on the error report, and exception-aware hosts should ignore it.
      */
     JS_ASSERT(reportp);
     if ((!callback || callback == js_GetErrorMessage) &&
@@ -600,16 +602,18 @@ ReportError(JSContext *cx, const char *m
 
 /*
  * The given JSErrorReport object have been zeroed and must not outlive
  * cx->fp() (otherwise report->originPrincipals may become invalid).
  */
 static void
 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
 {
+    AutoAssertNoGC nogc;
+
     /*
      * 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;
 
@@ -623,16 +627,18 @@ PopulateReportBlame(JSContext *cx, JSErr
  * 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"
  * type message, and then hope the process ends swiftly.
  */
 void
 js_ReportOutOfMemory(JSContext *cx)
 {
+    AutoAssertNoGC nogc;
+
     cx->runtime->hadOutOfMemory = true;
 
     JSErrorReport report;
     JSErrorReporter onError = cx->errorReporter;
 
     /* Get the message for this error, but we won't expand any arguments. */
     const JSErrorFormatString *efs =
         js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -405,18 +405,19 @@ JS_PUBLIC_API(JSBool)
 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
 {
     return fun->script()->bindings.count() > 0;
 }
 
 extern JS_PUBLIC_API(uintptr_t *)
 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
 {
+    RootedScript script(cx, fun->script());
     BindingVector bindings(cx);
-    if (!FillBindingVector(fun->script(), &bindings))
+    if (!FillBindingVector(script, &bindings))
         return NULL;
 
     /* Munge data into the API this method implements.  Avert your eyes! */
     *markp = cx->tempLifoAlloc().mark();
 
     uintptr_t *names = cx->tempLifoAlloc().newArray<uintptr_t>(bindings.length());
     if (!names) {
         js_ReportOutOfMemory(cx);
@@ -445,16 +446,17 @@ extern JS_PUBLIC_API(void)
 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
 {
     cx->tempLifoAlloc().release(mark);
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
 {
+    AutoAssertNoGC nogc;
     return fun->maybeScript();
 }
 
 JS_PUBLIC_API(JSNative)
 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
 {
     return fun->maybeNative();
 }
@@ -493,16 +495,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();
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fpArg)
 {
     /*
      * This API is used to compute the line number for jsd and XPConnect
@@ -527,27 +530,28 @@ JS_GetFrameAnnotation(JSContext *cx, JSS
     }
 
     return NULL;
 }
 
 JS_PUBLIC_API(void)
 JS_SetTopFrameAnnotation(JSContext *cx, void *annotation)
 {
+    AutoAssertNoGC nogc;
     StackFrame *fp = cx->fp();
     JS_ASSERT_IF(fp->beginsIonActivation(), !fp->annotation());
 
     // 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);
 
-    JSScript *script = fp->script();
+    RawScript script = fp->script();
 
     ReleaseAllJITCode(cx->runtime->defaultFreeOp());
 
     // Ensure that we'll never try to compile this again.
     JS_ASSERT(!script->hasIonScript());
     script->ion = ION_DISABLED_SCRIPT;
 }
 
@@ -666,16 +670,17 @@ JS_PUBLIC_API(jsval)
 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
 {
     return Valueify(fp)->returnValue();
 }
 
 JS_PUBLIC_API(void)
 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fpArg, jsval rval)
 {
+    AutoAssertNoGC nogc;
     StackFrame *fp = Valueify(fpArg);
 #ifdef JS_METHODJIT
     JS_ASSERT(fp->script()->debugMode);
 #endif
     assertSameCompartment(cx, fp, rval);
     fp->setReturnValue(rval);
 }
 
@@ -1001,19 +1006,18 @@ GetAtomTotalSize(JSContext *cx, JSAtom *
     return sizeof(AtomStateEntry) + sizeof(HashNumber) +
            sizeof(JSString) +
            (atom->length() + 1) * sizeof(jschar);
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
 {
-    size_t nbytes;
-
-    nbytes = sizeof *fun;
+    AutoAssertNoGC nogc;
+    size_t nbytes = sizeof *fun;
     nbytes += JS_GetObjectTotalSize(cx, fun);
     if (fun->isInterpreted())
         nbytes += JS_GetScriptTotalSize(cx, fun->script());
     if (fun->displayAtom())
         nbytes += GetAtomTotalSize(cx, fun->displayAtom());
     return nbytes;
 }
 
@@ -1170,37 +1174,40 @@ JS_PUBLIC_API(JSObject *)
 JS_UnwrapObjectAndInnerize(JSObject *obj)
 {
     return UnwrapObject(obj, /* stopAtOuter = */ false);
 }
 
 JS_FRIEND_API(JSBool)
 js_CallContextDebugHandler(JSContext *cx)
 {
+    AssertCanGC();
     ScriptFrameIter iter(cx);
     JS_ASSERT(!iter.done());
 
-    jsval rval;
-    switch (js::CallContextDebugHandler(cx, iter.script(), iter.pc(), &rval)) {
+    RootedValue rval(cx);
+    RootedScript script(cx, iter.script());
+    switch (js::CallContextDebugHandler(cx, script, iter.pc(), rval.address())) {
       case JSTRAP_ERROR:
         JS_ClearPendingException(cx);
         return JS_FALSE;
       case JSTRAP_THROW:
         JS_SetPendingException(cx, rval);
         return JS_FALSE;
       case JSTRAP_RETURN:
       case JSTRAP_CONTINUE:
       default:
         return JS_TRUE;
     }
 }
 
 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.fun = i.maybeCallee();
         if (!frames.append(desc))
@@ -1267,39 +1274,41 @@ FormatValue(JSContext *cx, const Value &
         return "[function]";
     return buf;
 }
 
 static char *
 FormatFrame(JSContext *cx, const ScriptFrameIter &iter, char *buf, int num,
             JSBool showArgs, JSBool showLocals, JSBool showThisProps)
 {
-    JSScript* script = iter.script();
+    AssertCanGC();
+
+    RootedScript script(cx, iter.script());
     jsbytecode* pc = iter.pc();
 
     RootedObject scopeChain(cx, iter.scopeChain());
     JSAutoCompartment ac(cx, scopeChain);
 
     const char *filename = script->filename;
     unsigned lineno = PCToLineNumber(script, pc);
-    JSFunction *fun = iter.maybeCallee();
-    JSString *funname = NULL;
+    RootedFunction fun(cx, iter.maybeCallee());
+    RootedString funname(cx);
     if (fun)
         funname = fun->atom();
 
-    JSObject *callObj = NULL;
+    RootedObject callObj(cx);
     AutoPropertyDescArray callProps(cx);
 
     if (!iter.isIon() && (showArgs || showLocals)) {
         callObj = JS_GetFrameCallObject(cx, Jsvalify(iter.interpFrame()));
         if (callObj)
             callProps.fetch(callObj);
     }
 
-    Value thisVal = UndefinedValue();
+    RootedValue thisVal(cx);
     AutoPropertyDescArray thisProps(cx);
     if (iter.computeThis()) {
         thisVal = iter.thisv();
         if (showThisProps && !thisVal.isPrimitive())
             thisProps.fetch(&thisVal.toObject());
     }
 
     // print the frame number and function name
@@ -1335,29 +1344,29 @@ FormatFrame(JSContext *cx, const ScriptF
                                     value ? value : "?unknown?",
                                     desc->value.isString() ? "\"" : "");
             if (!buf)
                 return buf;
             namedArgCount++;
         }
 
         // print any unnamed trailing args (found in 'arguments' object)
-        Value val;
-        if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) {
+        RootedValue val(cx);
+        if (JS_GetProperty(cx, callObj, "arguments", val.address()) && val.isObject()) {
             uint32_t argCount;
-            JSObject* argsObj = &val.toObject();
-            if (JS_GetProperty(cx, argsObj, "length", &val) &&
+            RootedObject argsObj(cx, &val.toObject());
+            if (JS_GetProperty(cx, argsObj, "length", val.address()) &&
                 ToUint32(cx, val, &argCount) &&
                 argCount > namedArgCount)
             {
                 for (uint32_t k = namedArgCount; k < argCount; k++) {
                     char number[8];
                     JS_snprintf(number, 8, "%d", (int) k);
 
-                    if (JS_GetProperty(cx, argsObj, number, &val)) {
+                    if (JS_GetProperty(cx, argsObj, number, val.address())) {
                         JSAutoByteString valueBytes;
                         const char *value = FormatValue(cx, val, valueBytes);
                         buf = JS_sprintf_append(buf, "%s%s%s%s",
                                                 k ? ", " : "",
                                                 val.isString() ? "\"" : "",
                                                 value ? value : "?unknown?",
                                                 val.isString() ? "\"" : "");
                         if (!buf)
@@ -1396,17 +1405,18 @@ FormatFrame(JSContext *cx, const ScriptF
             }
         }
     }
 
     // print the value of 'this'
     if (showLocals) {
         if (!thisVal.isUndefined()) {
             JSAutoByteString thisValBytes;
-            if (JSString* thisValStr = ToString(cx, thisVal)) {
+            RootedString thisValStr(cx, ToString(cx, thisVal));
+            if (thisValStr) {
                 if (const char *str = thisValBytes.encode(cx, thisValStr)) {
                     buf = JS_sprintf_append(buf, "    this = %s\n", str);
                     if (!buf)
                         return buf;
                 }
             }
         } else {
             buf = JS_sprintf_append(buf, "    <failed to get 'this' value>\n");
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -280,23 +280,24 @@ InitExnPrivate(JSContext *cx, HandleObje
             if (i.isNonEvalFunctionFrame()) {
                 RawAtom atom = i.callee()->displayAtom();
                 if (atom == NULL)
                     atom = cx->runtime->emptyString;
                 frame.funName = atom;
             } else {
                 frame.funName = NULL;
             }
-            const char *cfilename = i.script()->filename;
+            RootedScript script(cx, i.script());
+            const char *cfilename = script->filename;
             if (!cfilename)
                 cfilename = "";
             frame.filename = SaveScriptFilename(cx, cfilename);
             if (!frame.filename)
                 return false;
-            frame.ulineno = PCToLineNumber(i.script(), i.pc());
+            frame.ulineno = PCToLineNumber(script, i.pc());
         }
     }
 
     /* Do not need overflow check: the vm stack is already bigger. */
     JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame));
 
     size_t nbytes = offsetof(JSExnPrivate, stackElems) +
                     frames.length() * sizeof(JSStackTraceElem);
@@ -568,40 +569,41 @@ Exception(JSContext *cx, unsigned argc, 
 
     /* Find the scripted caller. */
     NonBuiltinScriptFrameIter iter(cx);
 
     /* XXX StackIter should not point directly to scripts. */
     SkipRoot skip(cx, &iter);
 
     /* Set the 'fileName' property. */
+    RootedScript script(cx, iter.script());
     RootedString filename(cx);
     if (args.length() > 1) {
         filename = ToString(cx, args[1]);
         if (!filename)
             return false;
         args[1].setString(filename);
     } else {
         filename = cx->runtime->emptyString;
         if (!iter.done()) {
-            if (const char *cfilename = iter.script()->filename) {
+            if (const char *cfilename = script->filename) {
                 filename = FilenameToString(cx, cfilename);
                 if (!filename)
                     return false;
             }
         }
     }
 
     /* Set the 'lineNumber' property. */
     uint32_t lineno, column = 0;
     if (args.length() > 2) {
         if (!ToUint32(cx, args[2], &lineno))
             return false;
     } else {
-        lineno = iter.done() ? 0 : PCToLineNumber(iter.script(), iter.pc(), &column);
+        lineno = iter.done() ? 0 : PCToLineNumber(script, iter.pc(), &column);
     }
 
     int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
     if (!InitExnPrivate(cx, obj, message, filename, lineno, column, NULL, exnType))
         return false;
 
     args.rval().setObject(*obj);
     return true;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -119,16 +119,18 @@ fun_getProperty(JSContext *cx, HandleObj
             return false;
         }
 
         ArgumentsObject *argsobj = ArgumentsObject::createUnexpected(cx, iter);
         if (!argsobj)
             return false;
 
 #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();
         if (!script->hasIonScript())
             ion::ForbidCompilation(cx, script);
 #endif
@@ -416,57 +418,62 @@ js::XDRInterpretedFunction(XDRState<mode
         fun->nargs = flagsword >> 16;
         fun->flags = uint16_t(flagsword);
         fun->initAtom(atom);
         fun->initScript(script);
         script->setFunction(fun);
         if (!JSFunction::setTypeForScriptedFunction(cx, fun))
             return false;
         JS_ASSERT(fun->nargs == fun->script()->bindings.numArgs());
-        js_CallNewScriptHook(cx, fun->script(), fun);
+        RootedScript script(cx, fun->script());
+        js_CallNewScriptHook(cx, script, fun);
         objp.set(fun);
     }
 
     return true;
 }
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, MutableHandleObject);
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, MutableHandleObject);
 
 JSObject *
 js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
 {
+    AssertCanGC();
+
     /* NB: Keep this in sync with XDRInterpretedFunction. */
 
     RootedObject parent(cx, NULL);
     RootedFunction
         clone(cx, js_NewFunction(cx, NullPtr(), NULL, 0, JSFUN_INTERPRETED, parent, NullPtr()));
     if (!clone)
         return NULL;
     if (!JSObject::clearParent(cx, clone))
         return NULL;
     if (!JSObject::clearType(cx, clone))
         return NULL;
 
-    RawScript clonedScript = CloneScript(cx, enclosingScope, clone, srcFun->script());
+    RootedScript srcScript(cx, srcFun->script());
+    RawScript clonedScript = CloneScript(cx, enclosingScope, clone, srcScript);
     if (!clonedScript)
         return NULL;
 
     clone->nargs = srcFun->nargs;
     clone->flags = srcFun->flags;
     clone->initAtom(srcFun->displayAtom());
     clone->initScript(clonedScript);
     clonedScript->setFunction(clone);
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return NULL;
 
-    js_CallNewScriptHook(cx, clone->script(), clone);
+    RootedScript cloneScript(cx, clone->script());
+    js_CallNewScriptHook(cx, cloneScript, clone);
     return clone;
 }
 
 /*
  * [[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.
  */
@@ -633,17 +640,17 @@ js::FunctionToString(JSContext *cx, Hand
     bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
     if (haveSource && !fun->script()->scriptSource()->hasSourceData() &&
         !fun->script()->loadSource(cx, &haveSource))
     {
         return NULL;
     }
     if (haveSource) {
         RootedScript script(cx, fun->script());
-        RootedString srcStr(cx, fun->script()->sourceData(cx));
+        RootedString srcStr(cx, script->sourceData(cx));
         if (!srcStr)
             return NULL;
         Rooted<JSStableString *> src(cx, srcStr->ensureStable(cx));
         if (!src)
             return NULL;
 
         StableCharPtr chars = src->chars();
         bool exprBody = fun->flags & JSFUN_EXPR_CLOSURE;
@@ -1093,17 +1100,19 @@ CallOrConstructBoundFunction(JSContext *
 }
 
 }
 
 #if JS_HAS_GENERATORS
 static JSBool
 fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
 {
-    JSFunction *fun;
+    AutoAssertNoGC nogc;
+
+    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();
@@ -1457,30 +1466,31 @@ js_NewFunction(JSContext *cx, HandleObje
 
     return fun;
 }
 
 JSFunction * JS_FASTCALL
 js_CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
                        HandleObject proto, gc::AllocKind kind)
 {
+    AssertCanGC();
     JS_ASSERT(parent);
     JS_ASSERT(proto);
     JS_ASSERT(!fun->isBoundFunction());
 
     RawObject cloneobj =
         NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
     if (!cloneobj)
         return NULL;
     RootedFunction clone(cx, cloneobj->toFunction());
 
     clone->nargs = fun->nargs;
     clone->flags = fun->flags & ~JSFUN_EXTENDED;
     if (fun->isInterpreted()) {
-        clone->initScript(fun->script());
+        clone->initScript(fun->script().unsafeGet());
         clone->initEnvironment(parent);
     } else {
         clone->initNative(fun->native(), fun->jitInfo());
     }
     clone->initAtom(fun->displayAtom());
 
     if (kind == JSFunction::ExtendedFinalizeKind) {
         clone->flags |= JSFUN_EXTENDED;
@@ -1522,18 +1532,19 @@ js_CloneFunctionObject(JSContext *cx, Ha
             if (!cscript)
                 return NULL;
 
             clone->setScript(cscript);
             cscript->setFunction(clone);
 
             GlobalObject *global = script->compileAndGo ? &script->global() : NULL;
 
-            js_CallNewScriptHook(cx, clone->script(), clone);
-            Debugger::onNewScript(cx, clone->script(), global);
+            script = clone->script();
+            js_CallNewScriptHook(cx, script, clone);
+            Debugger::onNewScript(cx, script, global);
         }
     }
     return clone;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
                   unsigned nargs, unsigned attrs, const char *selfHostedName, AllocKind kind)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -127,30 +127,30 @@ 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::HandleScript script() const {
+    js::Return<JSScript*> script() const {
         JS_ASSERT(isInterpreted());
         return JS::HandleScript::fromMarkedLocation(&u.i.script_);
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
         return *(js::HeapPtrScript *)&u.i.script_;
     }
 
     inline void setScript(JSScript *script_);
     inline void initScript(JSScript *script_);
 
-    JS::HandleScript maybeScript() const {
+    js::Return<JSScript*> maybeScript() const {
         return isInterpreted() ? script() : JS::NullPtr();
     }
 
     JSNative native() const {
         JS_ASSERT(isNative());
         return u.n.native;
     }
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4489,25 +4489,24 @@ BudgetIncrementalGC(JSRuntime *rt, int64
  * GC, repeatedly if necessary, until we think we have not created any new
  * garbage. We disable inlining to ensure that the bottom of the stack with
  * possible GC roots recorded in MarkRuntime excludes any pointers we use during
  * the marking implementation.
  */
 static JS_NEVER_INLINE void
 GCCycle(JSRuntime *rt, bool incremental, int64_t budget, JSGCInvocationKind gckind, gcreason::Reason reason)
 {
+    /* If we attempt to invoke the GC while we are running in the GC, assert. */
+    AutoAssertNoGC nogc;
+
 #ifdef DEBUG
     for (CompartmentsIter c(rt); !c.done(); c.next())
         JS_ASSERT_IF(rt->gcMode == JSGC_MODE_GLOBAL, c->isGCScheduled());
 #endif
 
-    /* Recursive GC is no-op. */
-    if (rt->isHeapBusy())
-        return;
-
     /* Don't GC if we are reporting an OOM. */
     if (rt->inOOMReport)
         return;
 
     AutoGCSession gcsession(rt);
 
     /*
      * As we about to purge caches and clear the mark bits we must wait for
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1228,16 +1228,17 @@ TypeConstraintSetElement::newType(JSCont
         type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
         objectTypes->addSetProperty(cx, script, pc, valueTypes, JSID_VOID);
     }
 }
 
 void
 TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
 {
+    AssertCanGC();
     RootedScript script(cx, callsite->script);
     jsbytecode *pc = callsite->pc;
 
     JS_ASSERT_IF(script->hasAnalysis(),
                  callsite->returnTypes == script->analysis()->bytecodeTypes(pc));
 
     if (type.isUnknown() || type.isAnyObject()) {
         /* Monitor calls on unknown functions. */
@@ -1316,36 +1317,37 @@ TypeConstraintCall::newType(JSContext *c
         callee = type.typeObject()->interpretedFunction;
         if (!callee)
             return;
     } else {
         /* Calls on non-objects are dynamically monitored. */
         return;
     }
 
-    if (!callee->script()->ensureHasTypes(cx))
+    RootedScript calleeScript(cx, callee->script());
+    if (!calleeScript->ensureHasTypes(cx))
         return;
 
     unsigned nargs = callee->nargs;
 
     /* Add bindings for the arguments of the call. */
     for (unsigned i = 0; i < callsite->argumentCount && i < nargs; i++) {
         StackTypeSet *argTypes = callsite->argumentTypes[i];
-        StackTypeSet *types = TypeScript::ArgTypes(callee->script(), i);
+        StackTypeSet *types = TypeScript::ArgTypes(calleeScript, i);
         argTypes->addSubsetBarrier(cx, script, pc, types);
     }
 
     /* Add void type for any formals in the callee not supplied at the call site. */
     for (unsigned i = callsite->argumentCount; i < nargs; i++) {
-        TypeSet *types = TypeScript::ArgTypes(callee->script(), i);
+        TypeSet *types = TypeScript::ArgTypes(calleeScript, i);
         types->addType(cx, Type::UndefinedType());
     }
 
-    StackTypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
-    HeapTypeSet *returnTypes = TypeScript::ReturnTypes(callee->script());
+    StackTypeSet *thisTypes = TypeScript::ThisTypes(calleeScript);
+    HeapTypeSet *returnTypes = TypeScript::ReturnTypes(calleeScript);
 
     if (callsite->isNew) {
         /*
          * If the script does not return a value then the pushed value is the
          * new object (typical case). Note that we don't model construction of
          * the new value, which is done dynamically; we don't keep track of the
          * possible 'new' types for a given prototype type object.
          */
@@ -1395,17 +1397,17 @@ TypeConstraintPropagateThis::newType(JSC
     } else {
         /* Ignore calls to primitives, these will go through a stub. */
         return;
     }
 
     if (!callee->script()->ensureHasTypes(cx))
         return;
 
-    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script());
+    TypeSet *thisTypes = TypeScript::ThisTypes(callee->script().unsafeGet());
     if (this->types)
         this->types->addSubset(cx, thisTypes);
     else
         thisTypes->addType(cx, this->type);
 }
 
 void
 TypeConstraintArith::newType(JSContext *cx, TypeSet *source, Type type)
@@ -1697,16 +1699,18 @@ HeapTypeSet::HasObjectFlags(JSContext *c
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
                       cx->compartment->types.compiledInfo, flags), false);
     return false;
 }
 
 static inline void
 ObjectStateChange(JSContext *cx, TypeObject *object, bool markingUnknown, bool force)
 {
+    AutoAssertNoGC nogc;
+
     if (object->unknownProperties())
         return;
 
     /* All constraints listening to state changes are on the empty id. */
     TypeSet *types = object->maybeGetProperty(cx, JSID_EMPTY);
 
     /* Mark as unknown after getting the types, to avoid assertion. */
     if (markingUnknown)
@@ -2533,16 +2537,17 @@ TypeCompartment::addPendingRecompile(JSC
     }
 
     co->setPendingRecompilation();
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(script);
     if (!constrainedOutputs)
         return;
 
 #ifdef JS_METHODJIT
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
@@ -2572,16 +2577,18 @@ TypeCompartment::addPendingRecompile(JSC
 # endif
 #endif
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, HandleScript script, uint32_t offset,
                                  bool returnOnly)
 {
+    AutoAssertNoGC nogc;
+
     if (!script->ensureRanInference(cx))
         return;
 
     ScriptAnalysis *analysis = script->analysis();
     jsbytecode *pc = script->code + offset;
 
     JS_ASSERT_IF(returnOnly, js_CodeSpec[*pc].format & JOF_INVOKE);
 
@@ -5571,17 +5578,17 @@ JSScript::makeAnalysis(JSContext *cx)
     }
 
     return true;
 }
 
 /* static */ bool
 JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton)
 {
-    JS_ASSERT(fun->script());
+    JS_ASSERT(fun->script().unsafeGet());
     JS_ASSERT(fun->script()->function() == fun);
 
     if (!cx->typeInferenceEnabled())
         return true;
 
     if (singleton) {
         if (!setSingletonType(cx, fun))
             return false;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -650,16 +650,18 @@ UseNewTypeAtEntry(JSContext *cx, StackFr
 
     RootedScript prevScript(cx, fp->prev()->script());
     return UseNewType(cx, prevScript, fp->prevpc());
 }
 
 inline bool
 UseNewTypeForClone(JSFunction *fun)
 {
+    AutoAssertNoGC nogc;
+
     if (fun->hasSingletonType() || !fun->isInterpreted())
         return false;
 
     /*
      * When a function is being used as a wrapper for another function, it
      * improves precision greatly to distinguish between different instances of
      * the wrapper; otherwise we will conflate much of the information about
      * the wrapped functions.
@@ -872,16 +874,17 @@ TypeScript::MonitorUnknown(JSContext *cx
 {
     if (cx->typeInferenceEnabled())
         TypeDynamicResult(cx, script, pc, Type::UnknownType());
 }
 
 /* static */ inline void
 TypeScript::GetPcScript(JSContext *cx, MutableHandleScript script, jsbytecode **pc)
 {
+    AutoAssertNoGC nogc;
 #ifdef JS_ION
     if (cx->fp()->beginsIonActivation()) {
         ion::GetPcScript(cx, script, pc);
         return;
     }
 #endif
     script.set(cx->fp()->script());
     *pc = cx->regs().pc;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -286,25 +286,25 @@ js::RunScript(JSContext *cx, HandleScrip
         ~CheckStackBalance() {
             JS_ASSERT(fp == cx->fp());
             JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators);
         }
     } check(cx);
 #endif
 
     SPSEntryMarker marker(cx->runtime);
-	
+
 #ifdef JS_ION
     if (ion::IsEnabled(cx)) {
         ion::MethodStatus status = ion::CanEnter(cx, script, fp, false);
         if (status == ion::Method_Error)
             return false;
         if (status == ion::Method_Compiled) {
             ion::IonExecStatus status = ion::Cannon(cx, fp);
-            
+
             // Note that if we bailed out, new inline frames may have been
             // pushed, so we interpret with the current fp.
             if (status == ion::IonExec_Bailout)
                 return Interpret(cx, fp, JSINTERP_REJOIN);
 
             return status != ion::IonExec_Error;
         }
     }
@@ -370,17 +370,18 @@ js::InvokeKernel(JSContext *cx, CallArgs
         return false;
 
     /* Get pointer to new frame/slots, prepare arguments. */
     InvokeFrameGuard ifg;
     if (!cx->stack.pushInvokeFrame(cx, args, initial, &ifg))
         return false;
 
     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
-    JSBool ok = RunScript(cx, fun->script(), ifg.fp());
+    RootedScript script(cx, fun->script());
+    JSBool ok = RunScript(cx, script, ifg.fp());
 
     /* Propagate the return value out. */
     args.rval().set(ifg.fp()->returnValue());
     JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
     return ok;
 }
 
 bool
@@ -786,30 +787,29 @@ js::UnwindScope(JSContext *cx, uint32_t 
             break;
         }
     }
 }
 
 void
 js::UnwindForUncatchableException(JSContext *cx, const FrameRegs &regs)
 {
-
     /* c.f. the regular (catchable) TryNoteIter loop in Interpret. */
     for (TryNoteIter tni(regs); !tni.done(); ++tni) {
         JSTryNote *tn = *tni;
         if (tn->kind == JSTRY_ITER) {
             Value *sp = regs.spForStackDepth(tn->stackDepth);
             UnwindIteratorForUncatchableException(cx, &sp[-1].toObject());
         }
     }
 }
 
 TryNoteIter::TryNoteIter(const FrameRegs &regs)
   : regs(regs),
-    script(regs.fp()->script()),
+    script(regs.fp()->script().unsafeGet()),
     pcOffset(regs.pc - script->main())
 {
     if (script->hasTrynotes()) {
         tn = script->trynotes()->vector;
         tnEnd = tn + script->trynotes()->length;
     } else {
         tn = tnEnd = NULL;
     }
@@ -1128,36 +1128,38 @@ js::Interpret(JSContext *cx, StackFrame 
         op = (JSOp) *regs.pc;                                                 \
         if ((n) <= 0)                                                         \
             goto check_backedge;                                              \
         DO_OP();                                                              \
     JS_END_MACRO
 
 #define SET_SCRIPT(s)                                                         \
     JS_BEGIN_MACRO                                                            \
+        EnterAssertNoGCScope();                                               \
         script = (s);                                                         \
         if (script->hasAnyBreakpointsOrStepMode() || script->hasScriptCounts) \
             interrupts.enable();                                              \
         JS_ASSERT_IF(interpMode == JSINTERP_SKIP_TRAP,                        \
                      script->hasAnyBreakpointsOrStepMode());                  \
+        LeaveAssertNoGCScope();                                               \
     JS_END_MACRO
 
     /* Repoint cx->regs to a local variable for faster access. */
     FrameRegs regs = cx->regs();
     PreserveRegsGuard interpGuard(cx, regs);
 
     /*
      * Help Debugger find frames running scripts that it has put in
      * single-step mode.
      */
     InterpreterFrames interpreterFrame(cx, &regs, interrupts);
 
     /* Copy in hot values that change infrequently. */
     JSRuntime *const rt = cx->runtime;
-    Rooted<JSScript*> script(cx);
+    RootedScript script(cx);
     SET_SCRIPT(regs.fp()->script());
 
     /* Reset the loop count on the script we're entering. */
     script->resetLoopCount();
 
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::INTERPRETER_START,
@@ -2377,19 +2379,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);
-    JSScript *newScript = fun->script();
-
-    if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, newScript, initial))
+    RawScript funScript = fun->script().unsafeGet();
+    if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
         goto error;
 
     SET_SCRIPT(regs.fp()->script());
     script->resetLoopCount();
 
 #ifdef JS_ION
     if (!newType && ion::IsEnabled(cx)) {
         ion::MethodStatus status = ion::CanEnter(cx, script, regs.fp(), newType);
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -107,34 +107,37 @@ ComputeThis(JSContext *cx, StackFrame *f
  * temporary, the arguments object has been created a some other failed guard
  * that called JSScript::argumentsOptimizationFailed. In this case, it is
  * always valid (and necessary) to replace JS_OPTIMIZED_ARGUMENTS with the real
  * arguments object.
  */
 static inline bool
 IsOptimizedArguments(StackFrame *fp, Value *vp)
 {
+    AutoAssertNoGC nogc;
     if (vp->isMagic(JS_OPTIMIZED_ARGUMENTS) && fp->script()->needsArgsObj())
         *vp = ObjectValue(fp->argsObj());
     return vp->isMagic(JS_OPTIMIZED_ARGUMENTS);
 }
 
 /*
  * One optimized consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) is f.apply.
  * However, this speculation must be guarded before calling 'apply' in case it
  * is not the builtin Function.prototype.apply.
  */
 static inline bool
 GuardFunApplyArgumentsOptimization(JSContext *cx)
 {
+    AssertCanGC();
     FrameRegs &regs = cx->regs();
     if (IsOptimizedArguments(regs.fp(), &regs.sp[-1])) {
         CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
         if (!IsNativeFunction(args.calleev(), js_fun_apply)) {
-            if (!JSScript::argumentsOptimizationFailed(cx, regs.fp()->script()))
+            RootedScript script(cx, regs.fp()->script());
+            if (!JSScript::argumentsOptimizationFailed(cx, script))
                 return false;
             regs.sp[-1] = ObjectValue(regs.fp()->argsObj());
         }
     }
     return true;
 }
 
 /*
@@ -502,17 +505,17 @@ DefVarOrConstOperation(JSContext *cx, Ha
     }
 
     return true;
 }
 
 inline void
 InterpreterFrames::enableInterruptsIfRunning(JSScript *script)
 {
-    if (script == regs->fp()->script())
+    if (regs->fp()->script() == script)
         enabler.enable();
 }
 
 static JS_ALWAYS_INLINE bool
 AddOperation(JSContext *cx, HandleScript script, jsbytecode *pc, const Value &lhs, const Value &rhs,
              Value *res)
 {
     if (lhs.isInt32() && rhs.isInt32()) {
@@ -753,16 +756,17 @@ GetObjectElementOperation(JSContext *cx,
     assertSameCompartment(cx, res);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 GetElementOperation(JSContext *cx, JSOp op, HandleValue lref, HandleValue rref,
                     MutableHandleValue res)
 {
+    AssertCanGC();
     JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     if (lref.isString() && rref.isInt32()) {
         JSString *str = lref.toString();
         int32_t i = rref.toInt32();
         if (size_t(i) < str->length()) {
             str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
             if (!str)
@@ -778,17 +782,18 @@ GetElementOperation(JSContext *cx, JSOp 
         if (rref.isInt32()) {
             int32_t i = rref.toInt32();
             if (i >= 0 && uint32_t(i) < fp->numActualArgs()) {
                 res.set(fp->unaliasedActual(i));
                 return true;
             }
         }
 
-        if (!JSScript::argumentsOptimizationFailed(cx, fp->script()))
+        RootedScript script(cx, fp->script());
+        if (!JSScript::argumentsOptimizationFailed(cx, script))
             return false;
 
         lval = ObjectValue(fp->argsObj());
     }
 
     bool isObject = lval.isObject();
     RootedObject obj(cx, ToObjectFromStack(cx, lval));
     if (!obj)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1498,16 +1498,18 @@ typedef enum JSGeneratorOp {
 /*
  * Start newborn or restart yielding generator and perform the requested
  * operation inside its frame.
  */
 static JSBool
 SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
                 JSGenerator *gen, const Value &arg)
 {
+    AssertCanGC();
+
     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NESTING_GENERATOR);
         return JS_FALSE;
     }
 
     /*
      * Write barrier is needed since the generator stack can be updated,
      * and it's not barriered in any other way. We need to do it before
@@ -1557,17 +1559,18 @@ SendToGenerator(JSContext *cx, JSGenerat
 
         StackFrame *fp = gfg.fp();
         gen->regs = cx->regs();
 
         cx->enterGenerator(gen);   /* OOM check above. */
         PropertyIteratorObject *enumerators = cx->enumerators;
         cx->enumerators = gen->enumerators;
 
-        ok = RunScript(cx, fp->script(), fp);
+        RootedScript script(cx, fp->script());
+        ok = RunScript(cx, script, fp);
 
         gen->enumerators = cx->enumerators;
         cx->enumerators = enumerators;
         cx->leaveGenerator(gen);
     }
 
     if (gen->fp->isYielding()) {
         /* Yield cannot fail, throw or be called on closing. */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5535,26 +5535,27 @@ js_DumpStackFrame(JSContext *cx, StackFr
     }
 }
 
 #endif /* DEBUG */
 
 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();
             sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
-                            depth, (i.isIon() ? 0 : i.interpFrame()),
-                            filename, line,
-                            i.script(), i.pc() - i.script()->code);
+                            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/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -366,17 +366,17 @@ js_Disassemble(JSContext *cx, HandleScri
 }
 
 JS_FRIEND_API(JSBool)
 js_DumpPC(JSContext *cx)
 {
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return JS_FALSE;
-    JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter);
+    JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script().unsafeGet(), true, cx->regs().pc, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
     return ok;
 }
 
 JS_FRIEND_API(JSBool)
 js_DumpScript(JSContext *cx, JSScript *script_)
 {
     Sprinter sprinter(cx);
@@ -1103,17 +1103,17 @@ js_NewPrinter(JSContext *cx, const char 
     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 (!SetPrinterLocalNames(cx, fun->script(), jp)) {
+        if (!SetPrinterLocalNames(cx, fun->script().unsafeGet(), jp)) {
             js_DestroyPrinter(jp);
             return NULL;
         }
     }
     return jp;
 }
 
 void
@@ -1759,17 +1759,17 @@ DecompileSwitch(SprintStack *ss, TableEn
 #define LOCAL_ASSERT_RV(expr, rv)                                             \
     LOCAL_ASSERT_CUSTOM(expr, return (rv))
 
 static JSAtom *
 GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
 {
     LOCAL_ASSERT_RV(jp->fun, NULL);
     LOCAL_ASSERT_RV(slot < jp->script->bindings.count(), NULL);
-    LOCAL_ASSERT_RV(jp->script == jp->fun->script(), NULL);
+    LOCAL_ASSERT_RV(jp->script == jp->fun->script().unsafeGet(), NULL);
     JSAtom *name = (*jp->localNames)[slot].name();
 #if !JS_HAS_DESTRUCTURING
     LOCAL_ASSERT_RV(name, NULL);
 #endif
     return name;
 }
 
 #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, "")
@@ -4700,20 +4700,20 @@ Decompile(SprintStack *ss, jsbytecode *p
                     /*
                      * All allocation when decompiling is LIFO, using malloc or,
                      * more commonly, arena-allocating from cx->tempLifoAlloc
                      * Therefore after InitSprintStack succeeds, we must release
                      * to mark before returning.
                      */
                     LifoAllocScope las(&cx->tempLifoAlloc());
                     outerLocalNames = jp->localNames;
-                    if (!SetPrinterLocalNames(cx, fun->script(), jp))
+                    if (!SetPrinterLocalNames(cx, fun->script().unsafeGet(), jp))
                         return NULL;
 
-                    inner = fun->script();
+                    inner = fun->script().unsafeGet();
                     if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
                         js_delete(jp->localNames);
                         jp->localNames = outerLocalNames;
                         return NULL;
                     }
                     ss2.inGenExp = JS_TRUE;
 
                     /*
@@ -5574,17 +5574,17 @@ js_DecompileFunctionBody(JSPrinter *jp)
 
     JS_ASSERT(jp->fun);
     JS_ASSERT(!jp->script);
     if (jp->fun->isNative() || jp->fun->isSelfHostedBuiltin()) {
         js_printf(jp, native_code_str);
         return JS_TRUE;
     }
 
-    script = jp->fun->script();
+    script = jp->fun->script().unsafeGet();
     return DecompileBody(jp, script, script->code);
 }
 
 JSBool
 js_DecompileFunction(JSPrinter *jp)
 {
     JSContext *cx = jp->sprinter.context;
 
@@ -6128,17 +6128,17 @@ FindStartPC(JSContext *cx, ScriptFrameIt
     jsbytecode *current = *valuepc;
 
     if (spindex == JSDVG_IGNORE_STACK)
         return true;
 
     *valuepc = NULL;
 
     PCStack pcstack;
-    if (!pcstack.init(cx, iter.script(), current))
+    if (!pcstack.init(cx, iter.script().unsafeGet(), current))
         return false;
 
     if (spindex == JSDVG_SEARCH_STACK) {
         size_t index = iter.numFrameSlots();
         Value s;
 
         // We search from fp->sp to base to find the most recently calculated
         // value matching v under assumption that it is it that caused
@@ -6155,16 +6155,17 @@ FindStartPC(JSContext *cx, ScriptFrameIt
         *valuepc = pcstack[spindex];
     }
     return true;
 }
 
 static bool
 DecompileExpressionFromStack(JSContext *cx, int spindex, int skipStackHits, Value v, char **res)
 {
+    AssertCanGC();
     JS_ASSERT(spindex < 0 ||
               spindex == JSDVG_IGNORE_STACK ||
               spindex == JSDVG_SEARCH_STACK);
 
     *res = NULL;
 
 #ifdef JS_MORE_DETERMINISTIC
     /*
@@ -6175,21 +6176,21 @@ DecompileExpressionFromStack(JSContext *
     return true;
 #endif
 
     ScriptFrameIter frameIter(cx);
 
     if (frameIter.done())
         return true;
 
-    JSScript *script = frameIter.script();
+    RootedScript script(cx, frameIter.script());
     jsbytecode *valuepc = frameIter.pc();
-    JSFunction *fun = frameIter.isFunctionFrame()
-                      ? frameIter.callee()
-                      : NULL;
+    RootedFunction fun(cx, frameIter.isFunctionFrame()
+                           ? frameIter.callee()
+                           : NULL);
 
     JS_ASSERT(script->code <= valuepc && valuepc < script->code + script->length);
 
     // Give up if in prologue.
     if (valuepc < script->main())
         return true;
 
     if (!FindStartPC(cx, frameIter, spindex, skipStackHits, v, &valuepc))
@@ -6205,16 +6206,17 @@ DecompileExpressionFromStack(JSContext *
 
     return ed.getOutput(res);
 }
 
 char *
 js::DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v,
                             HandleString fallbackArg, int skipStackHits)
 {
+    AssertCanGC();
     RootedString fallback(cx, fallbackArg);
     {
         char *result;
         if (!DecompileExpressionFromStack(cx, spindex, skipStackHits, v, &result))
             return NULL;
         if (result) {
             if (strcmp(result, "(intermediate value)"))
                 return result;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -408,17 +408,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
     uint32_t length, lineno, nslots;
     uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
     uint32_t prologLength, version;
     uint32_t ndefaults = 0;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext *cx = xdr->cx();
-    Rooted<JSScript*> script(cx);
+    RootedScript script(cx);
     nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
     jssrcnote *notes = NULL;
 
     /* XDR arguments and vars. */
     uint16_t nargs = 0, nvars = 0;
     uint32_t argsVars = 0;
     if (mode == XDR_ENCODE) {
         script = scriptp.get();
@@ -1768,16 +1768,18 @@ JSScript::isShortRunning()
            hasAnalysis() &&
            !analysis()->hasFunctionCalls() &&
            getMaxLoopCount() < 40;
 }
 
 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_;
@@ -2046,33 +2048,35 @@ js_GetScriptLineExtent(RawScript script)
     return 1 + lineno - script->lineno;
 }
 
 namespace js {
 
 unsigned
 CurrentLine(JSContext *cx)
 {
+    AutoAssertNoGC nogc;
     return PCToLineNumber(cx->fp()->script(), 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;
     }
 
-    RootedScript script(cx, iter.script());
+    RawScript script = iter.script();
     *file = script->filename;
     *linenop = PCToLineNumber(iter.script(), iter.pc());
     *origin = script->originPrincipals;
 }
 
 }  /* namespace js */
 
 template <class T>
@@ -2081,16 +2085,18 @@ Rebase(RawScript dst, RawScript src, T *
 {
     size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
     return reinterpret_cast<T *>(dst->data + off);
 }
 
 JSScript *
 js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src)
 {
+    AssertCanGC();
+
     /* NB: Keep this in sync with XDRScript. */
 
     uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
 
     /* Script data */
@@ -2544,16 +2550,17 @@ JSScript::setNeedsArgsObj(bool needsArgs
     JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
     needsArgsAnalysis_ = false;
     needsArgsObj_ = needsArgsObj;
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
 {
+    AssertCanGC();
     JS_ASSERT(script->analyzedArgsUsage());
     JS_ASSERT(script->argumentsHasVarBinding());
     JS_ASSERT(!script->isGenerator);
 
     /*
      * It is possible that the apply speculation 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
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -40,19 +40,20 @@ AliasedFormalIter::AliasedFormalIter(JSS
 extern void
 CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *linenop, JSPrincipals **origin);
 
 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);
-        JSScript *script = cx->fp()->script();
+        RawScript script = cx->fp()->script();
         *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
@@ -2355,24 +2355,26 @@ static const uint32_t ReplaceOptArg = 2;
  * object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
  * such cases, which are used by javascript packers (particularly the popular
  * Dean Edwards packer) to efficiently encode large scripts. We only handle the
  * code patterns generated by such packers here.
  */
 static JSObject *
 LambdaIsGetElem(JSObject &lambda)
 {
+    AutoAssertNoGC nogc;
+
     if (!lambda.isFunction())
         return NULL;
 
     JSFunction *fun = lambda.toFunction();
     if (!fun->isInterpreted())
         return NULL;
 
-    JSScript *script = fun->script();
+    RawScript script = fun->script();
     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/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1748,17 +1748,18 @@ ParseXMLSource(JSContext *cx, HandleStri
     xml = NULL;
     filename = NULL;
     lineno = 1;
     ScriptFrameIter i(cx);
     if (!i.done()) {
         op = (JSOp) *i.pc();
         if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
             filename = i.script()->filename;
-            lineno = PCToLineNumber(i.script(), i.pc());
+            RootedScript script(cx, i.script());
+            lineno = PCToLineNumber(script, i.pc());
             for (endp = srcp + srclen; srcp < endp; srcp++) {
                 if (*srcp == '\n')
                     --lineno;
             }
         }
     }
 
     {
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -138,16 +138,17 @@ class Assembler : public ValueAssembler
         stackAdjust(0),
 #ifdef DEBUG
         callIsAligned(false),
 #endif
         sps(sps),
         vmframe(vmframe),
         pc(NULL)
     {
+        AutoAssertNoGC nogc;
         startLabel = label();
         if (vmframe)
             sps->setPushed(vmframe->script());
     }
 
     Assembler(MJITInstrumentation *sps, jsbytecode **pc)
       : callPatches(SystemAllocPolicy()),
         availInCall(0),
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -130,16 +130,17 @@ class LinkerHelper : public JSC::LinkBuf
             js_ReportOutOfMemory(cx);
             return NULL;
         }
         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(),
                                label.executableAddress(), masm.size());
         return label;
     }
 
     void maybeLink(MaybeJump jump, JSC::CodeLocationLabel label) {
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -43,17 +43,17 @@ using namespace js::mjit;
 using namespace JSC;
 
 using ic::Repatcher;
 
 static jsbytecode *
 FindExceptionHandler(JSContext *cx)
 {
     StackFrame *fp = cx->fp();
-    JSScript *script = fp->script();
+    RootedScript script(cx, fp->script());
 
     if (!script->hasTrynotes())
         return NULL;
 
   error:
     if (cx->isExceptionPending()) {
         for (TryNoteIter tni(cx->regs()); !tni.done(); ++tni) {
             JSTryNote *tn = *tni;
@@ -180,30 +180,31 @@ stubs::HitStackQuota(VMFrame &f)
 
 /*
  * This function must only be called after the early prologue, since it depends
  * on fp->exec.fun.
  */
 void * JS_FASTCALL
 stubs::FixupArity(VMFrame &f, uint32_t nactual)
 {
+    AssertCanGC();
     JSContext *cx = f.cx;
     StackFrame *oldfp = f.fp();
 
     JS_ASSERT(nactual != oldfp->numFormalArgs());
 
     /*
      * Grossssss! *move* the stack frame. If this ends up being perf-critical,
      * we can figure out how to spot-optimize it. Be careful to touch only the
      * members that have been initialized by the caller and early prologue.
      */
     InitialFrameFlags initial = oldfp->initialFlags();
-    JSFunction *fun           = oldfp->fun();
-    JSScript *script          = fun->script();
-    void *ncode               = oldfp->nativeReturnAddress();
+    RootedFunction fun(cx, oldfp->fun());
+    RootedScript script(cx, fun->script());
+    void *ncode = oldfp->nativeReturnAddress();
 
     /* Pop the inline frame. */
     f.regs.popPartialFrame((Value *)oldfp);
 
     /* Reserve enough space for a callee frame. */
     CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
     StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
                                              script, ncode, initial, &f.stackLimit);
@@ -272,32 +273,33 @@ ShouldJaegerCompileCallee(JSContext *cx,
 #endif
     return true;
 }
 
 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);
-    JSFunction *newfun = args.callee().toFunction();
-    JSScript *newscript = newfun->script();
+    RootedFunction newfun(cx, args.callee().toFunction());
+    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());
 
     if (!types::TypeMonitorCall(cx, args, construct))
         return false;
 
     /* Try to compile if not already compiled. */
-    if (ShouldJaegerCompileCallee(cx, f.script(), newscript, f.jit())) {
+    if (ShouldJaegerCompileCallee(cx, f.script().unsafeGet(), newscript, f.jit())) {
         CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct,
                                             CompileRequest_JIT, f.fp());
         if (status == Compile_Error) {
             /* A runtime exception was thrown, get out. */
             return false;
         }
         if (status == Compile_Abort)
             *unjittable = true;
@@ -516,17 +518,18 @@ js_InternalThrow(VMFrame &f)
     for (;;) {
         if (cx->isExceptionPending()) {
             // Call the throw hook if necessary
             JSThrowHook handler = cx->runtime->debugHooks.throwHook;
             if (handler || !cx->compartment->getDebuggees().empty()) {
                 Value rval;
                 JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval);
                 if (st == JSTRAP_CONTINUE && handler) {
-                    st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval,
+                    RootedScript fscript(cx, cx->fp()->script());
+                    st = handler(cx, fscript, cx->regs().pc, &rval,
                                  cx->runtime->debugHooks.throwHookData);
                 }
 
                 switch (st) {
                 case JSTRAP_ERROR:
                     cx->clearPendingException();
                     break;
 
@@ -589,17 +592,17 @@ js_InternalThrow(VMFrame &f)
     }
 
     JS_ASSERT(&cx->regs() == &f.regs);
 
     if (!pc)
         return NULL;
 
     StackFrame *fp = cx->fp();
-    JSScript *script = fp->script();
+    RootedScript script(cx, fp->script());
 
     /*
      * Fall back to EnterMethodJIT and finish the frame in the interpreter.
      * With type inference enabled, we may wipe out all JIT code on the
      * stack without patching ncode values to jump to the interpreter, and
      * thus can only enter JIT code via EnterMethodJIT (which overwrites
      * its entry frame's ncode). See ClearAllFrames.
      */
@@ -649,17 +652,18 @@ stubs::CreateThis(VMFrame &f, JSObject *
     if (!obj)
         THROW();
     fp->thisValue() = ObjectValue(*obj);
 }
 
 void JS_FASTCALL
 stubs::ScriptDebugPrologue(VMFrame &f)
 {
-    Probes::enterScript(f.cx, f.script(), f.script()->function(), f.fp());
+    AssertCanGC();
+    Probes::enterScript(f.cx, f.script().unsafeGet(), f.script()->function(), f.fp());
     JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
     switch (status) {
       case JSTRAP_CONTINUE:
         break;
       case JSTRAP_RETURN:
         *f.returnAddressLocation() = f.cx->jaegerRuntime().forceReturnFromFastCall();
         return;
       case JSTRAP_ERROR:
@@ -675,38 +679,42 @@ 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());
 }
 
 void JS_FASTCALL
 stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
 {
+    AutoAssertNoGC nogc;
     Probes::exitScript(f.cx, f.script(), f.script()->function(), f.fp());
 }
 
 void JS_FASTCALL
 stubs::CrossChunkShim(VMFrame &f, void *edge_)
 {
+    AssertCanGC();
     DebugOnly<CrossChunkEdge*> edge = (CrossChunkEdge *) edge_;
 
     mjit::ExpandInlineFrames(f.cx->compartment);
 
-    JSScript *script = f.script();
+    RawScript script = f.script().unsafeGet();
     JS_ASSERT(edge->target < script->length);
     JS_ASSERT(script->code + edge->target == f.pc());
 
     CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(),
                                         CompileRequest_Interpreter, f.fp());
+    script = NULL;
     if (status == Compile_Error)
         THROW();
 
     void **addr = f.returnAddressLocation();
     *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
 
     f.fp()->setRejoin(StubRejoin(REJOIN_RESUME));
 }
@@ -911,17 +919,17 @@ js_InternalInterpret(void *returnData, v
         JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
         if (!obj)
             return js_InternalThrow(f);
         fp->thisValue() = ObjectValue(*obj);
         /* FALLTHROUGH */
       }
 
       case REJOIN_THIS_CREATED: {
-        Probes::enterScript(f.cx, f.script(), f.script()->function(), fp);
+        Probes::enterScript(f.cx, f.script().unsafeGet(), f.script()->function(), fp);
 
         if (script->debugMode) {
             JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
             switch (status) {
               case JSTRAP_CONTINUE:
                 break;
               case JSTRAP_RETURN: {
                 /* Advance to the JSOP_STOP at the end of the script. */
@@ -971,17 +979,17 @@ js_InternalInterpret(void *returnData, v
             RootedObject callee(cx, &fp->callee());
             JSObject *obj = js_CreateThisForFunction(cx, callee, types::UseNewTypeAtEntry(cx, fp));
             if (!obj)
                 return js_InternalThrow(f);
             fp->functionThis() = ObjectValue(*obj);
         }
         /* FALLTHROUGH */
       case REJOIN_EVAL_PROLOGUE:
-        Probes::enterScript(cx, f.script(), f.script()->function(), fp);
+        Probes::enterScript(cx, f.script().unsafeGet(), f.script()->function(), fp);
         if (cx->compartment->debugMode()) {
             JSTrapStatus status = ScriptDebugPrologue(cx, fp);
             switch (status) {
               case JSTRAP_CONTINUE:
                 break;
               case JSTRAP_RETURN:
                 return f.cx->jaegerRuntime().forceReturnFromFastCall();
               case JSTRAP_ERROR:
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1016,21 +1016,19 @@ JaegerRuntime::finish()
 
 extern "C" JSBool
 JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit);
 
 JaegerStatus
 mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimit, bool partial)
 {
 #ifdef JS_METHODJIT_SPEW
+    JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n",
+               fp->script()->filename, fp->script()->lineno);
     Profiler prof;
-    JSScript *script = fp->script();
-
-    JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n",
-               script->filename, script->lineno);
     prof.start();
 #endif
 
     JS_ASSERT(cx->fp() == fp);
 
     JSBool ok;
     {
         AssertCompartmentUnchanged pcc(cx);
@@ -1102,39 +1100,38 @@ CheckStackAndEnterMethodJIT(JSContext *c
 
     return EnterMethodJIT(cx, fp, code, stackLimit, partial);
 }
 
 JaegerStatus
 mjit::JaegerShot(JSContext *cx, bool partial)
 {
     StackFrame *fp = cx->fp();
-    JSScript *script = fp->script();
-    JITScript *jit = script->getJIT(fp->isConstructing(), cx->compartment->compileBarriers());
+    JITScript *jit = fp->script()->getJIT(fp->isConstructing(), cx->compartment->compileBarriers());
 
-    JS_ASSERT(cx->regs().pc == script->code);
+    JS_ASSERT(cx->regs().pc == fp->script()->code);
 
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::JM_START,
                         TraceLogging::JM_STOP,
-                        script);
+                        fp->script().unsafeGet());
 #endif
 
     return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry, partial);
 }
 
 JaegerStatus
 js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint, bool partial)
 {
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::JM_SAFEPOINT_START,
                         TraceLogging::JM_SAFEPOINT_STOP,
-                        cx->fp()->script());
+                        cx->fp()->script().unsafeGet());
 #endif
     return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint, partial);
 }
 
 NativeMapEntry *
 JITChunk::nmap() const
 {
     return (NativeMapEntry *)((char*)this + sizeof(*this));
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -213,17 +213,17 @@ struct VMFrame
      */
     StackFrame *fp() { return regs.fp(); }
     mjit::JITScript *jit() { return fp()->jit(); }
 
     inline mjit::JITChunk *chunk();
     inline unsigned chunkIndex();
 
     /* Get the inner script/PC in case of inlining. */
-    inline JSScript *script();
+    inline Return<JSScript*> script();
     inline jsbytecode *pc();
 
 #if defined(JS_CPU_SPARC)
     static const size_t offsetOfFp = 30 * sizeof(void *) + FrameRegs::offsetOfFp;
     static const size_t offsetOfInlined = 30 * sizeof(void *) + FrameRegs::offsetOfInlined;
 #elif defined(JS_CPU_MIPS)
     static const size_t offsetOfFp = 8 * sizeof(void *) + FrameRegs::offsetOfFp;
     static const size_t offsetOfInlined = 8 * sizeof(void *) + FrameRegs::offsetOfInlined;
@@ -1037,27 +1037,29 @@ VMFrame::chunk()
 }
 
 inline unsigned
 VMFrame::chunkIndex()
 {
     return jit()->chunkIndex(regs.pc);
 }
 
-inline JSScript *
+inline Return<JSScript*>
 VMFrame::script()
 {
+    AutoAssertNoGC nogc;
     if (regs.inlined())
         return chunk()->inlineFrames()[regs.inlined()->inlineIndex].fun->script();
     return fp()->script();
 }
 
 inline jsbytecode *
 VMFrame::pc()
 {
+    AutoAssertNoGC nogc;
     if (regs.inlined())
         return script()->code + regs.inlined()->pcOffset;
     return regs.pc;
 }
 
 } /* namespace js */
 
 inline void *
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -56,16 +56,18 @@ PatchGetFallback(VMFrame &f, ic::GetGlob
     Repatcher repatch(f.chunk());
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::Name));
     repatch.relink(ic->slowPathCall, fptr);
 }
 
 void JS_FASTCALL
 ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
 {
+    AssertCanGC();
+
     RootedObject obj(f.cx, &f.fp()->global());
     PropertyName *name = f.script()->getName(GET_UINT32_INDEX(f.pc()));
 
     RecompilationMonitor monitor(f.cx);
 
     Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
 
     if (monitor.recompiled()) {
@@ -95,17 +97,19 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlo
 
     /* Do load anyway... this time. */
     stubs::Name(f);
 }
 
 static void JS_FASTCALL
 DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
-    stubs::SetName(f, f.script()->getName(GET_UINT32_INDEX(f.pc())));
+    AssertCanGC();
+    RootedPropertyName name(f.cx, f.script()->getName(GET_UINT32_INDEX(f.pc())));
+    stubs::SetName(f, name);
 }
 
 static void
 PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
     Repatcher repatch(f.chunk());
     VoidStubSetGlobal stub = DisabledSetGlobal;
     JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
@@ -146,16 +150,18 @@ UpdateSetGlobalName(VMFrame &f, ic::SetG
                                               ic->vr.isTypeKnown());
 
     return Lookup_Cacheable;
 }
 
 void JS_FASTCALL
 ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
 {
+    AssertCanGC();
+
     RootedObject obj(f.cx, &f.fp()->global());
     RootedPropertyName name(f.cx, f.script()->getName(GET_UINT32_INDEX(f.pc())));
 
     RecompilationMonitor monitor(f.cx);
 
     Shape *shape = obj->nativeLookup(f.cx, NameToId(name));
 
     if (!monitor.recompiled()) {
@@ -413,16 +419,18 @@ NativeStubLinker::init(JSContext *cx)
  * observed types for the opcode or loads the result into a register pair
  * (it will go through a type barrier afterwards).
  */
 bool
 mjit::NativeStubEpilogue(VMFrame &f, Assembler &masm, NativeStubLinker::FinalJump *result,
                          int32_t initialFrameDepth, int32_t vpOffset,
                          MaybeRegisterID typeReg, MaybeRegisterID dataReg)
 {
+    AutoAssertNoGC nogc;
+
     /* Reload fp, which may have been clobbered by restoreStackBase(). */
     masm.loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
 
     Jump hasException = masm.branchTest32(Assembler::Zero, Registers::ReturnReg,
                                           Registers::ReturnReg);
 
     Address resultAddress(JSFrameReg, vpOffset);
 
@@ -824,16 +832,18 @@ class CallCompiler : public BaseCompiler
         repatch.relink(ic.oolJump(), cs);
 
         return true;
     }
 #endif
 
     bool generateFullCallStub(JSScript *script, uint32_t flags)
     {
+        AutoAssertNoGC nogc;
+
         /*
          * Create a stub that works with arity mismatches. Like the fast-path,
          * this allocates a frame on the caller side, but also performs extra
          * checks for compilability. Perhaps this should be a separate, shared
          * trampoline, but for now we generate it dynamically.
          */
         Assembler masm;
         InlineFrameAssembler inlFrame(masm, ic, flags);
@@ -969,16 +979,17 @@ class CallCompiler : public BaseCompiler
                    ic.funGuard.executableAddress(),
                    static_cast<void*>(ic.fastGuardedObject));
 
         return true;
     }
 
     bool generateStubForClosures(JSObject *obj)
     {
+        AutoAssertNoGC nogc;
         JS_ASSERT(ic.frameSize.isStatic());
 
         /* Slightly less fast path - guard on fun->script() instead. */
         Assembler masm;
 
         Registers tempRegs(Registers::AvailRegs);
         tempRegs.takeReg(ic.funObjReg);
 
@@ -1061,19 +1072,19 @@ class CallCompiler : public BaseCompiler
         types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
 
         /*
          * Native stubs are not generated for inline frames. The overhead of
          * bailing out from the IC is far greater than the time saved by
          * inlining the parent frame in the first place, so mark the immediate
          * caller as uninlineable.
          */
-        if (f.script()->function()) {
-            f.script()->uninlineable = true;
-            MarkTypeObjectFlags(cx, f.script()->function(), types::OBJECT_FLAG_UNINLINEABLE);
+        if (fscript->function()) {
+            fscript->uninlineable = true;
+            MarkTypeObjectFlags(cx, fscript->function(), types::OBJECT_FLAG_UNINLINEABLE);
         }
 
         /* Don't touch the IC if the call triggered a recompilation. */
         if (monitor.recompiled())
             return true;
 
         JS_ASSERT(!f.regs.inlined());
 
@@ -1099,25 +1110,25 @@ class CallCompiler : public BaseCompiler
          * store the return value than FASTCALLs, and without additional
          * information we cannot tell which one is active on a VMFrame.
          */
         masm.storePtr(ImmPtr((void *) ic.frameSize.rejoinState(f.pc(), true)),
                       FrameAddress(offsetof(VMFrame, stubRejoin)));
 
         /* N.B. After this call, the frame will have a dynamic frame size. */
         if (ic.frameSize.isDynamic()) {
-            masm.bumpStubCount(f.script(), f.pc(), Registers::tempCallReg());
+            masm.bumpStubCount(fscript, f.pc(), Registers::tempCallReg());
             masm.fallibleVMCall(cx->typeInferenceEnabled(),
                                 JS_FUNC_TO_DATA_PTR(void *, ic::SplatApplyArgs),
                                 f.regs.pc, NULL, initialFrameDepth);
         }
 
         Registers tempRegs = Registers::tempCallRegMask();
         RegisterID t0 = tempRegs.takeAnyReg().reg();
-        masm.bumpStubCount(f.script(), f.pc(), t0);
+        masm.bumpStubCount(fscript, f.pc(), t0);
 
         int32_t storeFrameDepth = ic.frameSize.isStatic() ? initialFrameDepth : -1;
         masm.setupFallibleABICall(cx->typeInferenceEnabled(), f.regs.pc, storeFrameDepth);
 
         /* Grab cx. */
 #ifdef JS_CPU_X86
         RegisterID cxReg = tempRegs.takeAnyReg().reg();
 #else
@@ -1224,16 +1235,18 @@ class CallCompiler : public BaseCompiler
         JSFunction *fun = ucr.fun;
 
         if (!ucr.codeAddr) {
             // No JM code is available for this script yet.
             if (ucr.unjittable)
                 disable();
 
 #ifdef JS_ION
+            AutoAssertNoGC nogc;
+
             // If the following conditions pass, try to inline a call into
             // an IonMonkey JIT'd function.
             if (!callingNew &&
                 fun &&
                 !ic.hasJMStub() &&
                 !ic.hasIonStub() &&
                 ic.frameSize.isStatic() &&
                 ic.frameSize.staticArgc() <= ion::SNAPSHOT_MAX_NARGS &&
@@ -1241,16 +1254,17 @@ class CallCompiler : public BaseCompiler
             {
                 if (!generateIonStub())
                     THROWV(NULL);
             }
 #endif
             return NULL;
         }
 
+        AutoAssertNoGC nogc;
         JS_ASSERT(fun);
         JSScript *script = fun->script();
         JS_ASSERT(script);
 
         uint32_t flags = callingNew ? StackFrame::CONSTRUCTING : 0;
 
         if (!ic.hit) {
             ic.hit = true;
@@ -1407,16 +1421,17 @@ ic::SplatApplyArgs(VMFrame &f)
 
     f.u.call.dynamicArgc = length;
     return true;
 }
 
 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();
 
     if (jit->argsCheckPool)
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -106,16 +106,17 @@ class PICStubCompiler : public BaseCompi
 
     bool hadGC() {
         return gcNumber != f.cx->runtime->gcNumber;
     }
 
   protected:
     void spew(const char *event, const char *op) {
 #ifdef JS_METHODJIT_SPEW
+        AutoAssertNoGC nogc;
         JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
                    type, event, op, f.script()->filename, CurrentLine(cx));
 #endif
     }
 };
 
 static bool
 GeneratePrototypeGuards(JSContext *cx, Vector<JSC::MacroAssembler::Jump,8> &mismatches, Assembler &masm,
@@ -723,16 +724,17 @@ struct GetPropHelper {
         }
         if (!IsCacheableProtoChain(obj, holder))
             return ic.disable(f, "non-native holder");
         shape = prop;
         return Lookup_Cacheable;
     }
 
     LookupStatus testForGet() {
+        AutoAssertNoGC nogc;
         if (!shape->hasDefaultGetter()) {
             if (shape->hasGetterValue()) {
                 JSObject *getterObj = shape->getterObject();
                 if (!getterObj->isFunction() || !getterObj->toFunction()->isNative())
                     return ic.disable(f, "getter object not a native function");
             }
             if (shape->hasSlot() && holder != obj)
                 return ic.disable(f, "slotful getter hook through prototype");
@@ -891,16 +893,18 @@ class GetPropCompiler : public PICStubCo
 
         disable("string object length done");
 
         return Lookup_Cacheable;
     }
 
     LookupStatus generateStringPropertyStub()
     {
+        AssertCanGC();
+
         if (!f.fp()->script()->compileAndGo)
             return disable("String.prototype without compile-and-go global");
 
         RecompilationMonitor monitor(f.cx);
 
         RootedObject obj(f.cx, f.fp()->global().getOrCreateStringPrototype(f.cx));
         if (!obj)
             return error();
@@ -1045,16 +1049,18 @@ class GetPropCompiler : public PICStubCo
 
         return Lookup_Cacheable;
     }
 
     /* For JSPropertyOp getters. */
     void generateGetterStub(Assembler &masm, Shape *shape, jsid userid,
                             Label start, Vector<Jump, 8> &shapeMismatches)
     {
+        AutoAssertNoGC nogc;
+
         /*
          * Getter hook needs to be called from the stub. The state is fully
          * synced and no registers are live except the result registers.
          */
         JS_ASSERT(pic.canCallHook);
         PropertyOp getter = shape->getterOp();
 
         masm.storePtr(ImmPtr((void *) REJOIN_NATIVE_GETTER),
@@ -1155,16 +1161,18 @@ class GetPropCompiler : public PICStubCo
 
         linkerEpilogue(linker, start, shapeMismatches);
     }
 
     /* For getters backed by a JSNative. */
     void generateNativeGetterStub(Assembler &masm, Shape *shape,
                                   Label start, Vector<Jump, 8> &shapeMismatches)
     {
+        AutoAssertNoGC nogc;
+
         /*
          * Getter hook needs to be called from the stub. The state is fully
          * synced and no registers are live except the result registers.
          */
         JS_ASSERT(pic.canCallHook);
 
         JSFunction *fun = shape->getterObject()->toFunction();
         Native native = fun->native();
@@ -1245,31 +1253,35 @@ class GetPropCompiler : public PICStubCo
 
         linker.patchJump(pic.fastPathRejoin);
 
         linkerEpilogue(linker, start, shapeMismatches);
     }
 
     LookupStatus generateStub(JSObject *holder, HandleShape shape)
     {
+        AssertCanGC();
         Vector<Jump, 8> shapeMismatches(cx);
 
         MJITInstrumentation sps(&f.cx->runtime->spsProfiler);
         Assembler masm(&sps, &f);
 
         // Ignore GC pointers baked into assembly visible on the stack.
         SkipRoot skip(cx, &masm);
 
         Label start;
         Jump shapeGuardJump;
         Jump argsLenGuard;
 
         bool setStubShapeOffset = true;
         if (obj->isDenseArray()) {
-            MarkNotIdempotent(f.script(), f.pc());
+            {
+                RawScript script = f.script().unsafeGet();
+                MarkNotIdempotent(script, f.pc());
+            }
 
             start = masm.label();
             shapeGuardJump = masm.branchPtr(Assembler::NotEqual,
                                             Address(pic.objReg, JSObject::offsetOfShape()),
                                             ImmPtr(obj->lastProperty()));
 
             /*
              * No need to assert validity of GETPROP_STUB_SHAPE_JUMP in this case:
@@ -1375,17 +1387,20 @@ class GetPropCompiler : public PICStubCo
             }
 
             pic.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
         } else {
             pic.secondShapeGuard = 0;
         }
 
         if (shape && !shape->hasDefaultGetter()) {
-            MarkNotIdempotent(f.script(), f.pc());
+            {
+                RawScript script = f.script().unsafeGet();
+                MarkNotIdempotent(script, f.pc());
+            }
 
             if (shape->hasGetterValue()) {
                 generateNativeGetterStub(masm, shape, start, shapeMismatches);
             } else {
                 jsid userid;
                 if (!shape->getUserId(cx, &userid))
                     return error();
                 generateGetterStub(masm, shape, userid, start, shapeMismatches);
@@ -1480,24 +1495,27 @@ class GetPropCompiler : public PICStubCo
         GetPropHelper<GetPropCompiler> getprop(cx, obj, name, *this, f);
         RecompilationMonitor monitor(cx);
         LookupStatus status = getprop.lookupAndTest();
 
         if (status != Lookup_Cacheable && status != Lookup_NoProperty) {
             /* Don't touch the IC if it may have been destroyed. */
             if (!monitor.recompiled())
                 pic.hadUncacheable = true;
-            MarkNotIdempotent(f.script(), f.pc());
+            RawScript script = f.script().unsafeGet();
+            MarkNotIdempotent(script, f.pc());
             return status;
         }
 
         // Mark as not idempotent to avoid recompilation in Ion Monkey
         // GetPropertyCache.
-        if (!obj->hasIdempotentProtoChain())
-            MarkNotIdempotent(f.script(), f.pc());
+        if (!obj->hasIdempotentProtoChain()) {
+            RawScript script = f.script().unsafeGet();
+            MarkNotIdempotent(script, f.pc());
+        }
 
         if (hadGC())
             return Lookup_Uncacheable;
 
         if (obj == getprop.holder &&
             getprop.shape->hasDefaultGetter() &&
             !pic.inlinePathPatched)
         {
@@ -2041,17 +2059,18 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
     if (!monitor.recompiled() && pic->shouldUpdate(f)) {
         GetPropCompiler cc(f, obj, *pic, name, stub);
         if (!cc.update())
             THROW();
     }
 
     RootedValue v(f.cx);
     if (cached) {
-        if (!GetPropertyOperation(f.cx, f.script(), f.pc(), &objval, &v))
+        RootedScript script(f.cx, f.script());
+        if (!GetPropertyOperation(f.cx, script, f.pc(), &objval, &v))
             THROW();
     } else {
         if (!JSObject::getProperty(f.cx, obj, obj, name, &v))
             THROW();
     }
 
     f.regs.sp[-1] = v;
 }
@@ -2165,25 +2184,29 @@ ic::BindName(VMFrame &f, ic::PICInfo *pi
 
     f.regs.sp[0].setObject(*obj);
 }
 
 void
 BaseIC::spew(VMFrame &f, const char *event, const char *message)
 {
 #ifdef JS_METHODJIT_SPEW
+    AutoAssertNoGC nogc;
     JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
                js_CodeName[JSOp(*f.pc())], event, message,
                f.cx->fp()->script()->filename, CurrentLine(f.cx));
 #endif
 }
 
 /* Total length of scripts preceding a frame. */
-inline uint32_t frameCountersOffset(VMFrame &f)
+inline uint32_t
+frameCountersOffset(VMFrame &f)
 {
+    AutoAssertNoGC nogc;
+
     JSContext *cx = f.cx;
 
     uint32_t offset = 0;
     if (cx->regs().inlined()) {
         offset += cx->fp()->script()->length;
         uint32_t index = cx->regs().inlined()->inlineIndex;
         InlineFrame *frames = f.chunk()->inlineFrames();
         for (unsigned i = 0; i < index; i++)
@@ -2408,17 +2431,18 @@ GetElementIC::attachGetProp(VMFrame &f, 
         buffer.link(*pj, slowPathStart);
     buffer.link(done, fastPathRejoin);
 
     CodeLocationLabel cs = buffer.finalize(f);
 #if DEBUG
     char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length());
     JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom %p (\"%s\") shape %p (%s: %d)\n",
                js_CodeName[JSOp(*f.pc())], cs.executableAddress(), (void*)name, chars,
-               (void*)holder->lastProperty(), cx->fp()->script()->filename, CurrentLine(cx));
+               (void*)holder->lastProperty(), cx->fp()->script()->filename,
+               CurrentLine(cx));
     js_free(chars);
 #endif
 
     // Update the inline guards, if needed.
     if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalShapeGuard()) {
         Repatcher repatcher(f.chunk());
 
         if (shouldPatchInlineTypeGuard()) {
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -131,16 +131,18 @@ Recompiler::patchNative(JSCompartment *c
     }
 
     JS_ASSERT(found);
 }
 
 void
 Recompiler::patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script)
 {
+    AutoAssertNoGC nogc;
+
     /*
      * Check if the VMFrame returns directly into the script's jitcode. This
      * depends on the invariant that f->fp() reflects the frame at the point
      * where the call occurred, irregardless of any frames which were pushed
      * inside the call.
      */
     StackFrame *fp = f->fp();
     void **addr = f->returnAddressLocation();
@@ -174,16 +176,18 @@ Recompiler::patchFrame(JSCompartment *co
             }
         }
     }
 }
 
 StackFrame *
 Recompiler::expandInlineFrameChain(StackFrame *outer, InlineFrame *inner)
 {
+    AutoAssertNoGC nogc;
+
     StackFrame *parent;
     if (inner->parent)
         parent = expandInlineFrameChain(outer, inner->parent);
     else
         parent = outer;
 
     JaegerSpew(JSpew_Recompile, "Expanding inline frame\n");
 
@@ -218,16 +222,17 @@ JITCodeReturnAddress(void *data)
  * Expand all inlined frames within fp per 'inlined' and update next and regs
  * to refer to the new innermost frame.
  */
 void
 Recompiler::expandInlineFrames(JSCompartment *compartment,
                                StackFrame *fp, mjit::CallSite *inlined,
                                StackFrame *next, VMFrame *f)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT_IF(next, next->prev() == fp && next->prevInline() == inlined);
 
     /*
      * Treat any frame expansion as a recompilation event, so that f.jit() is
      * stable if no recompilations have occurred.
      */
     compartment->types.frameExpansions++;
 
@@ -323,16 +328,17 @@ ExpandInlineFrames(JSCompartment *compar
             fp->setDownFramesExpanded();
         }
     }
 }
 
 void
 ClearAllFrames(JSCompartment *compartment)
 {
+    AutoAssertNoGC nogc;
     if (!compartment || !compartment->rt->hasJaegerRuntime())
         return;
 
     ExpandInlineFrames(compartment);
 
     compartment->types.recompilations++;
 
     for (VMFrame *f = compartment->rt->jaegerRuntime().activeFrame();
@@ -389,16 +395,17 @@ ClearAllFrames(JSCompartment *compartmen
  *
  * - For VMFrames whose entryncode address (the value of entryfp->ncode before
  *   being clobbered with JaegerTrampolineReturn) is in the original script,
  *   redirect that entryncode to the interpoline.
  */
 void
 Recompiler::clearStackReferences(FreeOp *fop, JSScript *script)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(script->hasMJITInfo());
 
     JaegerSpew(JSpew_Recompile, "recompiling script (file \"%s\") (line \"%d\") (length \"%d\") (usecount=\"%d\")\n",
                script->filename, script->lineno, script->length, (int) script->getUseCount());
 
     JSCompartment *comp = script->compartment();
     types::AutoEnterTypeInference enter(fop, comp);
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -69,21 +69,23 @@ JSObject * JS_FASTCALL
 stubs::BindGlobalName(VMFrame &f)
 {
     return &f.fp()->global();
 }
 
 void JS_FASTCALL
 stubs::SetName(VMFrame &f, PropertyName *name)
 {
+    AssertCanGC();
     JSContext *cx = f.cx;
     RootedObject scope(cx, &f.regs.sp[-2].toObject());
     HandleValue value = HandleValue::fromMarkedLocation(&f.regs.sp[-1]);
+    RootedScript fscript(cx, f.script());
 
-    if (!SetNameOperation(cx, f.script(), f.pc(), scope, value))
+    if (!SetNameOperation(cx, fscript, f.pc(), scope, value))
         THROW();
 
     f.regs.sp[-2] = f.regs.sp[-1];
 }
 
 void JS_FASTCALL
 stubs::Name(VMFrame &f)
 {
@@ -112,17 +114,17 @@ stubs::SetElem(VMFrame &f)
     FrameRegs &regs = f.regs;
 
     HandleValue objval = HandleValue::fromMarkedLocation(&regs.sp[-3]);
     Value &idval  = regs.sp[-2];
     RootedValue rval(cx, regs.sp[-1]);
 
     RootedId id(cx);
 
-    Rooted<JSObject*> obj(cx, ToObjectFromStack(cx, objval));
+    RootedObject obj(cx, ToObjectFromStack(cx, objval));
     if (!obj)
         THROW();
 
     if (!FetchElementId(f.cx, obj, idval, id.address(),
                         MutableHandleValue::fromMarkedLocation(&regs.sp[-2])))
     {
         THROW();
     }
@@ -281,27 +283,27 @@ stubs::Ursh(VMFrame &f)
     if (!f.regs.sp[-2].setNumber(uint32_t(u))) {
         RootedScript fscript(f.cx, f.script());
         TypeScript::MonitorOverflow(f.cx, fscript, f.pc());
     }
 }
 
 template<JSBool strict>
 void JS_FASTCALL
-stubs::DefFun(VMFrame &f, JSFunction *fun_)
+stubs::DefFun(VMFrame &f, JSFunction *funArg)
 {
     /*
      * A top-level function defined in Global or Eval code (see ECMA-262
      * Ed. 3), or else a SpiderMonkey extension: a named function statement in
      * a compound statement (not at the top statement level of global code, or
      * at the top level of a function body).
      */
     JSContext *cx = f.cx;
     StackFrame *fp = f.fp();
-    RootedFunction fun(f.cx, fun_);
+    RootedFunction fun(f.cx, funArg);
 
     /*
      * If static link is not current scope, clone fun's object to link to the
      * current scope via parent. We do this to enable sharing of compiled
      * functions among multiple equivalent scopes, amortizing the cost of
      * compilation over a number of executions.  Examples include XUL scripts
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
@@ -742,24 +744,27 @@ stubs::Mod(VMFrame &f)
         RootedScript fscript(cx, f.script());
         TypeScript::MonitorOverflow(cx, fscript, f.pc());
     }
 }
 
 void JS_FASTCALL
 stubs::DebuggerStatement(VMFrame &f, jsbytecode *pc)
 {
+    AssertCanGC();
     JSDebuggerHandler handler = f.cx->runtime->debugHooks.debuggerHandler;
     if (handler || !f.cx->compartment->getDebuggees().empty()) {
         JSTrapStatus st = JSTRAP_CONTINUE;
-        Value rval;
-        if (handler)
-            st = handler(f.cx, f.script(), pc, &rval, f.cx->runtime->debugHooks.debuggerHandlerData);
+        RootedValue rval(f.cx);
+        if (handler) {
+            RootedScript fscript(f.cx, f.script());
+            st = handler(f.cx, fscript, pc, rval.address(), f.cx->runtime->debugHooks.debuggerHandlerData);
+        }
         if (st == JSTRAP_CONTINUE)
-            st = Debugger::onDebuggerStatement(f.cx, &rval);
+            st = Debugger::onDebuggerStatement(f.cx, rval.address());
 
         switch (st) {
           case JSTRAP_THROW:
             f.cx->setPendingException(rval);
             THROW();
 
           case JSTRAP_RETURN:
             f.cx->clearPendingException();
@@ -785,17 +790,18 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 #ifdef JS_ION
 void JS_FASTCALL
 stubs::TriggerIonCompile(VMFrame &f)
 {
-    JSScript *script = f.script();
+    AssertCanGC();
+    RootedScript script(f.cx, f.script());
 
     if (ion::js_IonOptions.parallelCompilation) {
         JS_ASSERT(!script->ion);
 
         jsbytecode *osrPC = f.regs.pc;
         if (*osrPC != JSOP_LOOPENTRY)
             osrPC = NULL;
 
@@ -824,16 +830,17 @@ stubs::TriggerIonCompile(VMFrame &f)
 
     f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
 }
 #endif
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
+    AutoAssertNoGC nogc;
     ExpandInlineFrames(f.cx->compartment);
     Recompiler::clearStackReferences(f.cx->runtime->defaultFreeOp(), f.script());
     f.jit()->destroyChunk(f.cx->runtime->defaultFreeOp(), f.chunkIndex(), /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
@@ -846,18 +853,20 @@ stubs::Trap(VMFrame &f, uint32_t trapTyp
      */
     JSTrapStatus result = JSTRAP_CONTINUE;
     if (trapTypes & JSTRAP_SINGLESTEP) {
         /*
          * single step mode may be paused without recompiling by
          * setting the interruptHook to NULL.
          */
         JSInterruptHook hook = f.cx->runtime->debugHooks.interruptHook;
-        if (hook)
-            result = hook(f.cx, f.script(), f.pc(), &rval, f.cx->runtime->debugHooks.interruptHookData);
+        if (hook) {
+            RootedScript fscript(f.cx, f.script());
+            result = hook(f.cx, fscript, f.pc(), &rval, f.cx->runtime->debugHooks.interruptHookData);
+        }
 
         if (result == JSTRAP_CONTINUE)
             result = Debugger::onSingleStep(f.cx, &rval);
     }
 
     if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP))
         result = Debugger::onTrap(f.cx, &rval);
 
@@ -1029,17 +1038,18 @@ void JS_FASTCALL
 stubs::GetProp(VMFrame &f, PropertyName *name)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
 
     MutableHandleValue objval = MutableHandleValue::fromMarkedLocation(&f.regs.sp[-1]);
 
     RootedValue rval(cx);
-    if (!GetPropertyOperation(cx, f.script(), f.pc(), objval, &rval))
+    RootedScript fscript(cx, f.script());
+    if (!GetPropertyOperation(cx, fscript, f.pc(), objval, &rval))
         THROW();
 
     regs.sp[-1] = rval;
 }
 
 void JS_FASTCALL
 stubs::GetPropNoCache(VMFrame &f, PropertyName *name)
 {
@@ -1288,16 +1298,17 @@ FindNativeCode(VMFrame &f, jsbytecode *t
 
     JS_NOT_REACHED("Missing edge");
     return NULL;
 }
 
 void * JS_FASTCALL
 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
 {
+    AutoAssertNoGC nogc;
     jsbytecode *jpc = pc;
     JSScript *script = f.fp()->script();
 
     /* 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));
@@ -1540,36 +1551,36 @@ stubs::TypeBarrierHelper(VMFrame &f, uin
     result = f.regs.sp[0];
 
     /*
      * Break type barriers at this bytecode if we have added many objects to
      * the target already. This isn't needed if inference results for the
      * script have been destroyed, as we will reanalyze and prune type barriers
      * as they are regenerated.
      */
-    if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) {
+    RootedScript fscript(f.cx, f.script());
+    if (fscript->hasAnalysis() && fscript->analysis()->ranInference()) {
         AutoEnterTypeInference enter(f.cx);
-        f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false);
+        fscript->analysis()->breakTypeBarriers(f.cx, f.pc() - fscript->code, false);
     }
 
-    RootedScript fscript(f.cx, f.script());
     TypeScript::Monitor(f.cx, fscript, f.pc(), result);
 }
 
 void JS_FASTCALL
 stubs::StubTypeHelper(VMFrame &f, int32_t which)
 {
     const Value &result = f.regs.sp[which];
 
-    if (f.script()->hasAnalysis() && f.script()->analysis()->ranInference()) {
+    RootedScript fscript(f.cx, f.script());
+    if (fscript->hasAnalysis() && fscript->analysis()->ranInference()) {
         AutoEnterTypeInference enter(f.cx);
-        f.script()->analysis()->breakTypeBarriers(f.cx, f.pc() - f.script()->code, false);
+        fscript->analysis()->breakTypeBarriers(f.cx, f.pc() - fscript->code, false);
     }
 
-    RootedScript fscript(f.cx, f.script());
     TypeScript::Monitor(f.cx, fscript, f.pc(), result);
 }
 
 /*
  * Variant of TypeBarrierHelper for checking types after making a native call.
  * The stack is already correct, and no fixup should be performed.
  */
 void JS_FASTCALL
@@ -1612,19 +1623,20 @@ stubs::CheckArgumentTypes(VMFrame &f)
     ic::GenerateArgumentCheckStub(f);
 #endif
 }
 
 #ifdef DEBUG
 void JS_FASTCALL
 stubs::AssertArgumentTypes(VMFrame &f)
 {
+    AutoAssertNoGC nogc;
     StackFrame *fp = f.fp();
     JSFunction *fun = fp->fun();
-    JSScript *script = fun->script();
+    RawScript script = fun->script();
 
     /*
      * 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))
@@ -1644,31 +1656,32 @@ stubs::AssertArgumentTypes(VMFrame &f)
  * there is an invariant failure when initially entering a loop.
  */
 void JS_FASTCALL stubs::MissedBoundsCheckEntry(VMFrame &f) {}
 void JS_FASTCALL stubs::MissedBoundsCheckHead(VMFrame &f) {}
 
 void * JS_FASTCALL
 stubs::InvariantFailure(VMFrame &f, void *rval)
 {
+    AutoAssertNoGC nogc;
     /*
      * Patch this call to the return site of the call triggering the invariant
      * failure (or a MissedBoundsCheck* function if the failure occurred on
      * initial loop entry), and trigger a recompilation which will then
      * redirect to the rejoin point for that call. We want to make things look
      * to the recompiler like we are still inside that call, and that after
      * 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. */
-    JSScript *script = f.fp()->script();
+    RawScript script = f.fp()->script();
     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/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1307,21 +1307,21 @@ AssertJit(JSContext *cx, unsigned argc, 
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 static JSScript *
 ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL)
 {
-    JSFunction *fun = JS_ValueToFunction(cx, v);
+    RootedFunction fun(cx, JS_ValueToFunction(cx, v));
     if (!fun)
         return NULL;
 
-    JSScript *script = fun->maybeScript();
+    RootedScript script(cx, fun->maybeScript());
     if (!script)
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_SCRIPTS_ONLY);
 
     if (fun && funp)
         *funp = fun;
 
     return script;
 }
@@ -1394,17 +1394,17 @@ TrapHandler(JSContext *cx, JSScript *, j
 {
     JSString *str = JSVAL_TO_STRING(closure);
 
     ScriptFrameIter iter(cx);
     JS_ASSERT(!iter.done());
 
     /* Debug-mode currently disables Ion compilation. */
     JSStackFrame *caller = Jsvalify(iter.interpFrame());
-    JSScript *script = iter.script();
+    RawScript script = iter.script().unsafeGet();
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return JSTRAP_ERROR;
 
     if (!JS_EvaluateUCInStackFrame(cx, caller, chars, length,
                                    script->filename,
@@ -1773,21 +1773,21 @@ DisassembleScript(JSContext *cx, JSScrip
     if (!js_Disassemble(cx, script, lines, sp))
         return false;
     SrcNotes(cx, script, sp);
     TryNotes(cx, script, sp);
 
     if (recursive && script->hasObjects()) {
         ObjectArray *objects = script->objects();
         for (unsigned i = 0; i != objects->length; ++i) {
-            JSObject *obj = objects->vector[i];
+            RawObject obj = objects->vector[i];
             if (obj->isFunction()) {
                 Sprint(sp, "\n");
-                JSFunction *fun = obj->toFunction();
-                JSScript *nested = fun->maybeScript();
+                RawFunction fun = obj->toFunction();
+                RawScript nested = fun->maybeScript().unsafeGet();
                 if (!DisassembleScript(cx, nested, fun, lines, recursive, sp))
                     return false;
             }
         }
     }
     return true;
 }
 
@@ -2532,19 +2532,20 @@ EvalInFrame(JSContext *cx, unsigned argc
         saved = JS_SaveFrameChain(cx);
 
     size_t length;
     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
     if (!chars)
         return false;
 
     StackFrame *fp = fi.interpFrame();
+    RootedScript fpscript(cx, fp->script());
     bool ok = !!JS_EvaluateUCInStackFrame(cx, Jsvalify(fp), chars, length,
-                                          fp->script()->filename,
-                                          JS_PCToLineNumber(cx, fp->script(),
+                                          fpscript->filename,
+                                          JS_PCToLineNumber(cx, fpscript,
                                                             fi.pc()),
                                           vp);
 
     if (saved)
         JS_RestoreFrameChain(cx);
 
     return ok;
 }
--- a/js/src/vm/ArgumentsObject-inl.h
+++ b/js/src/vm/ArgumentsObject-inl.h
@@ -64,31 +64,33 @@ ArgumentsObject::setArg(unsigned i, cons
     HeapValue &lhs = data()->args[i];
     JS_ASSERT(!lhs.isMagic(JS_FORWARD_TO_CALL_OBJECT));
     lhs = v;
 }
 
 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++) {
             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++) {
             if (fi.frameIndex() == i) {
                 callobj.setAliasedVar(fi, v);
                 return;
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -43,17 +43,18 @@ CopyStackFrameArguments(const StackFrame
         while (src != end)
             (dst++)->init(*src++);
     }
 }
 
 /* static */ void
 ArgumentsObject::MaybeForwardToCallObject(StackFrame *fp, JSObject *obj, ArgumentsData *data)
 {
-    JSScript *script = fp->script();
+    AutoAssertNoGC nogc;
+    RawScript script = fp->script();
     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
@@ -114,16 +115,18 @@ struct CopyStackIterArgs
     }
 };
 
 template <typename CopyArgs>
 /* static */ ArgumentsObject *
 ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals,
                         CopyArgs &copy)
 {
+    AssertCanGC();
+
     RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
     if (!proto)
         return NULL;
 
     RootedTypeObject type(cx, proto->getNewType(cx));
     if (!type)
         return NULL;
 
@@ -153,17 +156,17 @@ ArgumentsObject::create(JSContext *cx, H
 
     /* Copy [0, numArgs) into data->slots. */
     HeapValue *dst = data->args, *dstEnd = data->args + numArgs;
     copy.copyArgs(dst);
 
     data->deletedBits = reinterpret_cast<size_t *>(dstEnd);
     ClearAllBitArrayElements(data->deletedBits, numDeletedWords);
 
-    JSObject *obj = JSObject::create(cx, FINALIZE_KIND, shape, type, NULL);
+    RawObject obj = JSObject::create(cx, FINALIZE_KIND, shape, type, NULL);
     if (!obj)
         return NULL;
 
     obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
     obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
 
     copy.maybeForwardToCallObject(obj, data);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -567,17 +567,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
     }
 
     /*
      * Clean up all Debugger.Frame instances. Use a fresh FrameRange, as one
      * debugger's onPop handler could have caused another debugger to create its
      * own Debugger.Frame instance.
      */
     for (FrameRange r(fp, global); !r.empty(); r.popFront()) {
-        JSObject *frameobj = r.frontFrame();
+        RootedObject frameobj(cx, r.frontFrame());
         Debugger *dbg = r.frontDebugger();
         JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
 
         frameobj->setPrivate(NULL);
 
         /* If this frame had an onStep handler, adjust the script's count. */
         if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
             !fp->script()->changeStepModeCount(cx, -1))
@@ -589,17 +589,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
         dbg->frames.remove(fp);
     }
 
     /*
      * If this is an eval frame, then from the debugger's perspective the
      * script is about to be destroyed. Remove any breakpoints in it.
      */
     if (fp->isEvalFrame()) {
-        JSScript *script = fp->script();
+        RootedScript script(cx, fp->script());
         script->clearBreakpointsIn(cx->runtime->defaultFreeOp(), NULL, NULL);
     }
 
     /* Establish (status, value) as our resumption value. */
     switch (status) {
       case JSTRAP_RETURN:
         fp->setReturnValue(value);
         return true;
@@ -1037,19 +1037,18 @@ AddNewScriptRecipients(GlobalObject::Deb
         {
             return false;
         }
     }
     return true;
 }
 
 void
-Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script_, GlobalObject *compileAndGoGlobal_)
+Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal_)
 {
-    Rooted<JSScript*> script(cx, script_);
     Rooted<GlobalObject*> compileAndGoGlobal(cx, compileAndGoGlobal_);
 
     JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal);
 
     /*
      * Build the list of recipients. For compile-and-go scripts, this is the
      * same as the generic Debugger::dispatchHook code, but non-compile-and-go
      * scripts are not tied to particular globals. We deliver them to every
@@ -1081,17 +1080,17 @@ Debugger::slowPathOnNewScript(JSContext 
         }
     }
 }
 
 JSTrapStatus
 Debugger::onTrap(JSContext *cx, Value *vp)
 {
     StackFrame *fp = cx->fp();
-    Rooted<JSScript*> script(cx, fp->script());
+    RootedScript script(cx, fp->script());
     Rooted<GlobalObject*> scriptGlobal(cx, &fp->global());
     jsbytecode *pc = cx->regs().pc;
     BreakpointSite *site = script->getBreakpointSite(pc);
     JSOp op = JSOp(*pc);
 
     /* Build list of breakpoint handlers. */
     Vector<Breakpoint *> triggered(cx);
     for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
@@ -1135,17 +1134,18 @@ Debugger::onTrap(JSContext *cx, Value *v
                 return st;
 
             /* Calling JS code invalidates site. Reload it. */
             site = script->getBreakpointSite(pc);
         }
     }
 
     if (site && site->trapHandler) {
-        JSTrapStatus st = site->trapHandler(cx, fp->script(), pc, vp, site->trapClosure);
+        RootedScript fpscript(cx, fp->script());
+        JSTrapStatus st = site->trapHandler(cx, fpscript, pc, vp, site->trapClosure);
         if (st != JSTRAP_CONTINUE)
             return st;
     }
 
     /* By convention, return the true op to the interpreter in vp. */
     vp->setInt32(op);
     return JSTRAP_CONTINUE;
 }
@@ -1188,16 +1188,17 @@ Debugger::onSingleStep(JSContext *cx, Va
      * we're not receiving traps we didn't ask for. Even when frames is
      * non-empty (and thus we know this trap was requested), do the check
      * 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();
         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;
@@ -2232,37 +2233,39 @@ class Debugger::ScriptQuery {
         return matchAllDebuggeeGlobals();
     }
 
     /*
      * Search all relevant compartments and the stack for scripts matching
      * this query, and append the matching scripts to |vector|.
      */
     bool findScripts(AutoScriptVector *vector) {
+        AutoAssertNoGC nogc;
+
         if (!prepareQuery())
             return false;
 
         /* Search each compartment for debuggee scripts. */
         for (CompartmentSet::Range r = compartments.all(); !r.empty(); r.popFront()) {
             for (gc::CellIter i(r.front(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-                JSScript *script = i.get<JSScript>();
+                RawScript script = i.get<JSScript>();
                 if (script->compileAndGo && !script->isForEval()) {
                     if (!consider(script, &script->global(), vector))
                         return false;
                 }
             }
         }
 
         /*
          * Since eval scripts have no global, we need to find them via the call
          * stack, where frame's scope tells us the global in use.
          */
         for (ScriptFrameIter fri(cx); !fri.done(); ++fri) {
             if (fri.isEvalFrame()) {
-                JSScript *script = fri.script();
+                RawScript script = fri.script();
 
                 /*
                  * Eval scripts were not considered above so we don't need to
                  * check the existing script vector for duplicates.
                  */
                 JS_ASSERT(script->isForEval());
 
                 GlobalObject *global = &fri.interpFrame()->global();
@@ -2734,23 +2737,25 @@ DebuggerScript_getChildScripts(JSContext
         return false;
     if (script->hasObjects()) {
         /*
          * script->savedCallerFun indicates that this is a direct eval script
          * and the calling function is stored as script->objects()->vector[0].
          * It is not really a child script of this script, so skip it.
          */
         ObjectArray *objects = script->objects();
-        Rooted<JSScript*> funScript(cx);
+        RootedFunction fun(cx);
+        RootedScript funScript(cx);
+        RootedObject obj(cx), s(cx);
         for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
-            JSObject *obj = objects->vector[i];
+            obj = objects->vector[i];
             if (obj->isFunction()) {
-                JSFunction *fun = static_cast<JSFunction *>(obj);
+                fun = static_cast<JSFunction *>(obj.get());
                 funScript = fun->script();
-                JSObject *s = dbg->wrapScript(cx, funScript);
+                s = dbg->wrapScript(cx, funScript);
                 if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
                     return false;
             }
         }
     }
     args.rval().setObject(*result);
     return true;
 }
@@ -3328,25 +3333,26 @@ Class DebuggerArguments_class = {
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
 static JSBool
 DebuggerArguments_getArg(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     CallArgs args = CallArgsFromVp(argc, vp);
     int32_t i = args.callee().toFunction()->getExtendedSlot(0).toInt32();
 
     /* Check that the this value is an Arguments object. */
     if (!args.thisv().isObject()) {
         ReportObjectRequired(cx);
         return false;
     }
-    JSObject *argsobj = &args.thisv().toObject();
+    RootedObject argsobj(cx, &args.thisv().toObject());
     if (argsobj->getClass() != &DebuggerArguments_class) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                              "Arguments", "getArgument", argsobj->getClass()->name);
         return false;
     }
 
     /*
      * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
@@ -3355,36 +3361,37 @@ DebuggerArguments_getArg(JSContext *cx, 
     args.setThis(argsobj->getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME));
     THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, fp);
 
     /*
      * Since getters can be extracted and applied to other objects,
      * there is no guarantee this object has an ith argument.
      */
     JS_ASSERT(i >= 0);
-    Value arg;
+    RootedValue arg(cx);
+    RootedScript script(cx);
     if (unsigned(i) < fp->numActualArgs()) {
-        JSScript *script = fp->script();
+        script = fp->script();
         if (unsigned(i) < fp->numFormalArgs() && script->formalIsAliased(i)) {
             for (AliasedFormalIter fi(script); ; fi++) {
                 if (fi.frameIndex() == unsigned(i)) {
                     arg = fp->callObj().aliasedVar(fi);
                     break;
                 }
             }
         } else if (script->argsObjAliasesFormals() && fp->hasArgsObj()) {
             arg = fp->argsObj().arg(i);
         } else {
             arg = fp->unaliasedActual(i, DONT_CHECK_ALIASING);
         }
     } else {
         arg.setUndefined();
     }
 
-    if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg))
+    if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, arg.address()))
         return false;
     args.rval().set(arg);
     return true;
 }
 
 static JSBool
 DebuggerFrame_getArguments(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -3446,44 +3453,45 @@ DebuggerFrame_getArguments(JSContext *cx
 }
 
 static JSBool
 DebuggerFrame_getScript(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "get script", args, thisobj, fp);
     Debugger *debug = Debugger::fromChildJSObject(thisobj);
 
-    JSObject *scriptObject = NULL;
+    RootedObject scriptObject(cx);
     if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
         JSFunction &callee = fp->callee();
         if (callee.isInterpreted()) {
-            Rooted<JSScript*> script(cx, callee.script());
+            RootedScript script(cx, callee.script());
             scriptObject = debug->wrapScript(cx, script);
             if (!scriptObject)
                 return false;
         }
     } else {
         /*
          * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
          * frames.
          */
-        Rooted<JSScript*> script(cx, fp->script());
+        RootedScript script(cx, fp->script());
         scriptObject = debug->wrapScript(cx, script);
         if (!scriptObject)
             return false;
     }
     args.rval().setObjectOrNull(scriptObject);
     return true;
 }
 
 static JSBool
 DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "get offset", args, thisobj, fp);
-    JSScript *script = fp->script();
+    AutoAssertNoGC nogc;
+    RawScript script = fp->script();
     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;
 }
 
@@ -3777,16 +3785,17 @@ Class DebuggerObject_class = {
     NULL,                 /* construct   */
     NULL,                 /* hasInstance */
     DebuggerObject_trace
 };
 
 static JSObject *
 DebuggerObject_checkThis(JSContext *cx, const CallArgs &args, const char *fnname)
 {
+    AssertCanGC();
     if (!args.thisv().isObject()) {
         ReportObjectRequired(cx);
         return NULL;
     }
     JSObject *thisobj = &args.thisv().toObject();
     if (thisobj->getClass() != &DebuggerObject_class) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                              "Debugger.Object", fnname, thisobj->getClass()->name);
@@ -3821,23 +3830,25 @@ DebuggerObject_checkThis(JSContext *cx, 
         return false;                                                         \
     Debugger *dbg = Debugger::fromChildJSObject(obj);                         \
     obj = (JSObject *) obj->getPrivate();                                     \
     JS_ASSERT(obj)
 
 static JSBool
 DebuggerObject_construct(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Object");
     return false;
 }
 
 static JSBool
 DebuggerObject_getProto(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get proto", args, dbg, refobj);
     RootedObject proto(cx);
     {
         AutoCompartment ac(cx, refobj);
         if (!JSObject::getProto(cx, refobj, &proto))
             return false;
     }
     Value protov = ObjectOrNullValue(proto);
@@ -3845,16 +3856,17 @@ DebuggerObject_getProto(JSContext *cx, u
         return false;
     args.rval().set(protov);
     return true;
 }
 
 static JSBool
 DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
     const char *s = refobj->getClass()->name;
     JSAtom *str = Atomize(cx, s, strlen(s));
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
@@ -3865,16 +3877,17 @@ DebuggerObject_getCallable(JSContext *cx
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get callable", args, refobj);
     args.rval().setBoolean(refobj->isCallable());
     return true;
 }
 
 static JSBool
 DebuggerObject_getName(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get name", args, dbg, obj);
     if (!obj->isFunction()) {
         args.rval().setUndefined();
         return true;
     }
 
     JSString *name = obj->toFunction()->atom();
     if (!name) {
@@ -3887,16 +3900,17 @@ DebuggerObject_getName(JSContext *cx, un
         return false;
     args.rval().set(namev);
     return true;
 }
 
 static JSBool
 DebuggerObject_getDisplayName(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get display name", args, dbg, obj);
     if (!obj->isFunction()) {
         args.rval().setUndefined();
         return true;
     }
 
     JSString *name = obj->toFunction()->displayAtom();
     if (!name) {
@@ -3909,16 +3923,17 @@ DebuggerObject_getDisplayName(JSContext 
         return false;
     args.rval().set(namev);
     return true;
 }
 
 static JSBool
 DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get parameterNames", args, obj);
     if (!obj->isFunction()) {
         args.rval().setUndefined();
         return true;
     }
 
     RootedFunction fun(cx, obj->toFunction());
     JSObject *result = NewDenseAllocatedArray(cx, fun->nargs);
@@ -3950,41 +3965,43 @@ DebuggerObject_getParameterNames(JSConte
 
     args.rval().setObject(*result);
     return true;
 }
 
 static JSBool
 DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
 
     if (!obj->isFunction()) {
         args.rval().setUndefined();
         return true;
     }
 
-    JSFunction *fun = obj->toFunction();
+    RootedFunction fun(cx, obj->toFunction());
     if (!fun->isInterpreted()) {
         args.rval().setUndefined();
         return true;
     }
 
-    Rooted<JSScript*> script(cx, fun->script());
-    JSObject *scriptObject = dbg->wrapScript(cx, script);
+    RootedScript script(cx, fun->script());
+    RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
     if (!scriptObject)
         return false;
 
     args.rval().setObject(*scriptObject);
     return true;
 }
 
 static JSBool
 DebuggerObject_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
 
     /* Don't bother switching compartments just to check obj's type and get its env. */
     if (!obj->isFunction() || !obj->toFunction()->isInterpreted()) {
         args.rval().setUndefined();
         return true;
     }
 
@@ -4009,16 +4026,17 @@ DebuggerObject_getGlobal(JSContext *cx, 
         return false;
     args.rval().set(v);
     return true;
 }
 
 static JSBool
 DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj);
 
     RootedId id(cx);
     if (!ValueToId(cx, argc >= 1 ? args[0] : UndefinedValue(), id.address()))
         return false;
 
     /* Bug: This can cause the debuggee to run! */
     AutoPropertyDescriptorRooter desc(cx);
@@ -4052,16 +4070,17 @@ DebuggerObject_getOwnPropertyDescriptor(
     }
 
     return NewPropertyDescriptorObject(cx, &desc, args.rval().address());
 }
 
 static JSBool
 DebuggerObject_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
 {
+    AssertCanGC();
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyNames", args, dbg, obj);
 
     AutoIdVector keys(cx);
     {
         Maybe<AutoCompartment> ac;
         ac.construct(cx, obj);
         ErrorCopier ec(ac, dbg->toJSObject());
         if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -187,17 +187,17 @@ class Debugger {
     static JSPropertySpec properties[];
     static JSFunctionSpec methods[];
 
     JSObject *getHook(Hook hook) const;
     bool hasAnyLiveHooks() const;
 
     static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, Value *vp);
     static bool slowPathOnLeaveFrame(JSContext *cx, bool ok);
-    static void slowPathOnNewScript(JSContext *cx, JSScript *script,
+    static void slowPathOnNewScript(JSContext *cx, HandleScript script,
                                     GlobalObject *compileAndGoGlobal);
     static bool slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
     static JSTrapStatus dispatchHook(JSContext *cx, Value *vp, Hook which);
 
     JSTrapStatus fireDebuggerStatement(JSContext *cx, Value *vp);
     JSTrapStatus fireExceptionUnwind(JSContext *cx, Value *vp);
     JSTrapStatus fireEnterFrame(JSContext *cx, Value *vp);
     JSTrapStatus fireNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global, Value *vp);
@@ -251,17 +251,17 @@ class Debugger {
     static void sweepAll(FreeOp *fop);
     static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
                                              GlobalObjectSet::Enum *compartmentEnum);
 
     static inline JSTrapStatus onEnterFrame(JSContext *cx, Value *vp);
     static inline bool onLeaveFrame(JSContext *cx, bool ok);
     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, Value *vp);
     static inline JSTrapStatus onExceptionUnwind(JSContext *cx, Value *vp);
-    static inline void onNewScript(JSContext *cx, JSScript *script,
+    static inline void onNewScript(JSContext *cx, HandleScript script,
                                    GlobalObject *compileAndGoGlobal);
     static inline bool onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
     static JSTrapStatus onTrap(JSContext *cx, Value *vp);
     static JSTrapStatus onSingleStep(JSContext *cx, Value *vp);
 
     /************************************* Functions for use by Debugger.cpp. */
 
     inline bool observesEnterFrame() const;
@@ -539,17 +539,17 @@ JSTrapStatus
 Debugger::onExceptionUnwind(JSContext *cx, Value *vp)
 {
     return cx->compartment->getDebuggees().empty()
            ? JSTRAP_CONTINUE
            : dispatchHook(cx, vp, OnExceptionUnwind);
 }
 
 void
-Debugger::onNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal)
+Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal)
 {
     JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal);
     JS_ASSERT_IF(!script->compileAndGo, !compileAndGoGlobal);
     if (!script->compartment()->getDebuggees().empty())
         slowPathOnNewScript(cx, script, compileAndGoGlobal);
 }
 
 bool
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -199,31 +199,33 @@ GlobalObject::initFunctionAndObjectClass
      * objects in JSON and script literals.
      */
     if (!objectProto->setNewTypeUnknown(cx))
         return NULL;
 
     /* Create |Function.prototype| next so we can create other functions. */
     RootedFunction functionProto(cx);
     {
-        JSObject *functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self);
+        RawObject functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self);
         if (!functionProto_)
             return NULL;
         functionProto = functionProto_->toFunction();
 
         /*
          * Bizarrely, |Function.prototype| must be an interpreted function, so
          * give it the guts to be one.
          */
-        JSObject *proto = js_NewFunction(cx, functionProto,
-                                         NULL, 0, JSFUN_INTERPRETED, self, NullPtr());
-        if (!proto)
-            return NULL;
-        JS_ASSERT(proto == functionProto);
-        functionProto->flags |= JSFUN_PROTOTYPE;
+        {
+            RawObject proto = js_NewFunction(cx, functionProto,
+                                             NULL, 0, JSFUN_INTERPRETED, self, NullPtr());
+            if (!proto)
+                return NULL;
+            JS_ASSERT(proto == functionProto);
+            functionProto->flags |= JSFUN_PROTOTYPE;
+        }
 
         const char *rawSource = "() {\n}";
         size_t sourceLen = strlen(rawSource);
         jschar *source = InflateString(cx, rawSource, &sourceLen);
         if (!source)
             return NULL;
         ScriptSource *ss = cx->new_<ScriptSource>();
         if (!ss) {
@@ -231,24 +233,24 @@ GlobalObject::initFunctionAndObjectClass
             return NULL;
         }
         ScriptSourceHolder ssh(cx->runtime, ss);
         ss->setSource(source, sourceLen);
 
         CompileOptions options(cx);
         options.setNoScriptRval(true)
                .setVersion(JSVERSION_DEFAULT);
-        Rooted<JSScript*> script(cx, JSScript::Create(cx,
-                                                      /* enclosingScope = */ NullPtr(),
-                                                      /* savedCallerFun = */ false,
-                                                      options,
-                                                      /* staticLevel = */ 0,
-                                                      ss,
-                                                      0,
-                                                      ss->length()));
+        RootedScript script(cx, JSScript::Create(cx,
+                                                 /* enclosingScope = */ NullPtr(),
+                                                 /* savedCallerFun = */ false,
+                                                 options,
+                                                 /* staticLevel = */ 0,
+                                                 ss,
+                                                 0,
+                                                 ss->length()));
         if (!script || !JSScript::fullyInitTrivial(cx, script))
             return NULL;
 
         functionProto->initScript(script);
         functionProto->getType(cx)->interpretedFunction = functionProto;
         script->setFunction(functionProto);
 
         if (!JSObject::setSingletonType(cx, functionProto))
@@ -342,28 +344,26 @@ GlobalObject::initFunctionAndObjectClass
         !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
         !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) ||
         !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL))
     {
         return NULL;
     }
 
     /* Add the global Function and Object properties now. */
-    jsid objectId = NameToId(cx->names().Object);
-    if (!self->addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, NameToId(cx->names().Object), JSProto_Object + JSProto_LIMIT * 2, 0))
         return NULL;
-    jsid functionId = NameToId(cx->names().Function);
-    if (!self->addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, NameToId(cx->names().Function), JSProto_Function + JSProto_LIMIT * 2, 0))
         return NULL;
 
     /* Heavy lifting done, but lingering tasks remain. */
 
     /* ES5 15.1.2.1. */
-    RootedId id(cx, NameToId(cx->names().eval));
-    JSObject *evalobj = js_DefineFunction(cx, self, id, IndirectEval, 1, JSFUN_STUB_GSOPS);
+    RootedId evalId(cx, NameToId(cx->names().eval));
+    RawObject evalobj = js_DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
     if (!evalobj)
         return NULL;
     self->setOriginalEval(evalobj);
 
     /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
     RootedFunction throwTypeError(cx, js_NewFunction(cx, NullPtr(), ThrowTypeError, 0, 0, self,
                                                      NullPtr()));
     if (!throwTypeError)
@@ -396,17 +396,18 @@ GlobalObject::initFunctionAndObjectClass
     Rooted<TaggedProto> tagged(cx, TaggedProto(objectProto));
     if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, tagged))
         return NULL;
 
     /*
      * Notify any debuggers about the creation of the script for
      * |Function.prototype| -- after all initialization, for simplicity.
      */
-    js_CallNewScriptHook(cx, functionProto->script(), functionProto);
+    RootedScript functionProtoScript(cx, functionProto->script());
+    js_CallNewScriptHook(cx, functionProtoScript, functionProto);
     return functionProto;
 }
 
 GlobalObject *
 GlobalObject::create(JSContext *cx, Class *clasp)
 {
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -79,16 +79,17 @@ StaticScopeIter::block() const
 {
     JS_ASSERT(type() == BLOCK);
     return obj->asStaticBlock();
 }
 
 JSScript *
 StaticScopeIter::funScript() const
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(type() == FUNCTION);
     return obj->toFunction()->script();
 }
 
 /*****************************************************************************/
 
 StaticScopeIter
 js::ScopeCoordinateToStaticScope(JSScript *script, jsbytecode *pc)
@@ -179,67 +180,70 @@ CallObject::createTemplateObject(JSConte
 
 /*
  * Construct a call object for the given bindings.  If this is a call object
  * for a function invocation, callee should be the function being called.
  * Otherwise it must be a call object for eval of strict mode code, and callee
  * must be null.
  */
 CallObject *
-CallObject::create(JSContext *cx, JSScript *script, HandleObject enclosing, HandleFunction callee)
+CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
 {
     CallObject *callobj = CallObject::createTemplateObject(cx, script);
     if (!callobj)
         return NULL;
 
     callobj->asScope().setEnclosingScope(enclosing);
     callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
     return callobj;
 }
 
 CallObject *
 CallObject::createForFunction(JSContext *cx, StackFrame *fp)
 {
+    AssertCanGC();
     JS_ASSERT(fp->isNonEvalFunctionFrame());
     assertSameCompartment(cx, fp);
 
     RootedObject scopeChain(cx, fp->scopeChain());
 
     /*
      * For a named function expression Call's parent points to an environment
      * object holding function's name.
      */
     if (fp->fun()->isNamedLambda()) {
         scopeChain = DeclEnvObject::create(cx, fp);
         if (!scopeChain)
             return NULL;
     }
 
     RootedScript script(cx, fp->script());
-    Rooted<JSFunction*> callee(cx, &fp->callee());
+    RootedFunction callee(cx, &fp->callee());
     CallObject *callobj = create(cx, script, scopeChain, callee);
     if (!callobj)
         return NULL;
 
     /* Copy in the closed-over formal arguments. */
     for (AliasedFormalIter i(script); i; i++)
         callobj->setAliasedVar(i, fp->unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
 
     return callobj;
 }
 
 CallObject *
 CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
 {
+    AssertCanGC();
     JS_ASSERT(fp->isStrictEvalFrame());
     JS_ASSERT(cx->fp() == fp);
     JS_ASSERT(cx->regs().pc == fp->script()->code);
 
-    Rooted<JSFunction*> callee(cx, NULL);
-    return create(cx, fp->script(), fp->scopeChain(), callee);
+    RootedFunction callee(cx);
+    RootedScript script(cx, fp->script());
+    return create(cx, script, fp->scopeChain(), callee);
 }
 
 JS_PUBLIC_DATA(Class) js::CallClass = {
     "Call",
     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
@@ -309,17 +313,17 @@ WithObject::create(JSContext *cx, Handle
 
     RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, shape, type, NULL));
     if (!obj)
         return NULL;
 
     obj->asScope().setEnclosingScope(enclosing);
     obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
 
-    JSObject *thisp = JSObject::thisObject(cx, proto);
+    RawObject thisp = JSObject::thisObject(cx, proto);
     if (!thisp)
         return NULL;
 
     obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
 
     return &obj->asWith();
 }
 
@@ -625,16 +629,17 @@ ClonedBlockObject::create(JSContext *cx,
     JS_ASSERT(obj->isDelegate());
 
     return &obj->asClonedBlock();
 }
 
 void
 ClonedBlockObject::copyUnaliasedValues(StackFrame *fp)
 {
+    AutoAssertNoGC nogc;
     StaticBlockObject &block = staticBlock();
     unsigned base = fp->script()->nfixed + block.stackDepth();
     for (unsigned i = 0; i < slotCount(); ++i) {
         if (!block.isAliased(i))
             setVar(i, fp->unaliasedLocal(base + i), DONT_CHECK_ALIASING);
     }
 }
 
@@ -970,16 +975,17 @@ ScopeIter::operator++()
         break;
     }
     return *this;
 }
 
 void
 ScopeIter::settle()
 {
+    AutoAssertNoGC nogc;
     /*
      * Given an iterator state (cur_, block_), figure out which (potentially
      * optimized) scope the iterator should report. Thus, the result is a pair
      * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the
      * scope object has been optimized away and does not exist on the scope
      * chain. Beware: while ScopeIter iterates over the scopes of a single
      * frame, the scope chain (pointed to by cur_) continues into the scopes of
      * enclosing frames. Thus, it is important not to look at cur_ until it is
@@ -1191,22 +1197,23 @@ class DebugScopeProxy : public BaseProxy
 
         /* Handle unaliased let and catch bindings at block scope. */
         if (scope->isClonedBlock()) {
             ClonedBlockObject &block = scope->asClonedBlock();
             Shape *shape = block.lastProperty()->search(cx, id);
             if (!shape)
                 return false;
 
+            AutoAssertNoGC nogc;
             unsigned i = shape->shortid();
             if (block.staticBlock().isAliased(i))
                 return false;
 
             if (maybefp) {
-                JSScript *script = maybefp->script();
+                RawScript script = maybefp->script();
                 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)
@@ -1249,23 +1256,23 @@ class DebugScopeProxy : public BaseProxy
      * This function creates an arguments object when the debugger requests
      * 'arguments' for a function scope where the arguments object has been
      * optimized away (either because the binding is missing altogether or
      * because !ScriptAnalysis::needsArgsObj).
      */
     static bool checkForMissingArguments(JSContext *cx, jsid id, ScopeObject &scope,
                                          ArgumentsObject **maybeArgsObj)
     {
+        AssertCanGC();
         *maybeArgsObj = NULL;
 
         if (!isArguments(cx, id) || !isFunctionScope(scope))
             return true;
 
-        JSScript *script = scope.asCall().callee().script();
-        if (script->needsArgsObj())
+        if (scope.asCall().callee().script()->needsArgsObj())
             return true;
 
         StackFrame *maybefp = cx->runtime->debugScopes->hasLiveFrame(scope);
         if (!maybefp) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_LIVE,
                                  "Debugger scope");
             return false;
         }
@@ -1706,29 +1713,29 @@ DebugScopes::onPopCall(StackFrame *fp, J
         StackFrame::CopyVector vec;
         if (!fp->copyRawFrameSlots(&vec) || vec.length() == 0)
             return;
 
         /*
          * Copy in formals that are not aliased via the scope chain
          * but are aliased via the arguments object.
          */
-        JSScript *script = fp->script();
+        RootedScript script(cx, fp->script());
         if (script->needsArgsObj() && fp->hasArgsObj()) {
             for (unsigned i = 0; i < fp->numFormalArgs(); ++i) {
                 if (script->formalLivesInArgumentsObject(i))
                     vec[i] = fp->argsObj().arg(i);
             }
         }
 
         /*
          * Use a dense array as storage (since proxies do not have trace
          * hooks). This array must not escape into the wild.
          */
-        JSObject *snapshot = NewDenseCopiedArray(cx, vec.length(), vec.begin());
+        RootedObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
         if (!snapshot) {
             cx->clearPendingException();
             return;
         }
 
         debugScope->initSnapshot(*snapshot);
     }
 }
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -176,17 +176,17 @@ class ScopeObject : public JSObject
     }
 };
 
 class CallObject : public ScopeObject
 {
     static const uint32_t CALLEE_SLOT = 1;
 
     static CallObject *
-    create(JSContext *cx, JSScript *script, HandleObject enclosing, HandleFunction callee);
+    create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
 
   public:
     /* These functions are internal and are exposed only for JITs. */
     static CallObject *
     create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
 
     static CallObject *
     createTemplateObject(JSContext *cx, JSScript *script);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -69,18 +69,18 @@ StackFrame::compartment() const
     JS_ASSERT(scopeChain()->compartment() == script()->compartment());
     return scopeChain()->compartment();
 }
 
 #ifdef JS_METHODJIT
 inline mjit::JITScript *
 StackFrame::jit()
 {
-    JSScript *script_ = script();
-    return script_->getJIT(isConstructing(), script_->compartment()->compileBarriers());
+    AutoAssertNoGC nogc;
+    return script()->getJIT(isConstructing(), script()->compartment()->compileBarriers());
 }
 #endif
 
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
     JS_ASSERT(flags_ & HAS_PREVPC);
     if (FrameRegs *regs = cx->maybeRegs()) {
@@ -128,16 +128,17 @@ StackFrame::resetInlinePrev(StackFrame *
     prevpc_ = prevpc;
     prevInline_ = NULL;
 }
 
 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());
 
     /* Initialize stack frame members. */
     flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
@@ -187,40 +188,43 @@ StackFrame::jitHeavyweightFunctionProlog
     flags_ |= HAS_CALL_OBJ;
 
     return true;
 }
 
 inline void
 StackFrame::initVarsToUndefined()
 {
+    AutoAssertNoGC nogc;
     SetValueRangeToUndefined(slots(), script()->nfixed);
 }
 
 inline JSObject *
 StackFrame::createRestParameter(JSContext *cx)
 {
     JS_ASSERT(fun()->hasRest());
     unsigned nformal = fun()->nargs - 1, nactual = numActualArgs();
     unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
     return NewDenseCopiedArray(cx, nrest, actuals() + nformal);
 }
 
 inline Value &
 StackFrame::unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
     JS_ASSERT(i < script()->nfixed);
     return slots()[i];
 }
 
 inline Value &
 StackFrame::unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing)
 {
 #ifdef DEBUG
+    AutoAssertNoGC nogc;
     if (checkAliasing) {
         JS_ASSERT(i < script()->nslots);
         if (i < script()->nfixed) {
             JS_ASSERT(!script()->varIsAliased(i));
         } else {
             unsigned depth = i - script()->nfixed;
             for (StaticBlockObject *b = maybeBlockChain(); b; b = b->enclosingBlock()) {
                 if (b->containsVarAtDepth(depth)) {
@@ -374,42 +378,45 @@ StackFrame::callObj() const
 }
 
 /*****************************************************************************/
 
 STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
 JS_ALWAYS_INLINE bool
 StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
 {
+    AssertCanGC();
     assertInvariants();
     JS_ASSERT(from >= firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd_);
 #endif
     if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
         return ensureSpaceSlow(cx, report, from, nvals);
     return true;
 }
 
 inline Value *
 StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
 {
+    AssertCanGC();
     FrameRegs &regs = cx->regs();
     unsigned nvals = regs.fp()->script()->nslots + STACK_JIT_EXTRA;
     return ensureSpace(cx, report, regs.sp, nvals)
            ? conservativeEnd_
            : NULL;
 }
 
 /*****************************************************************************/
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
                            JSFunction *fun, JSScript *script, StackFrame::Flags *flags) const
 {
+    AssertCanGC();
     JS_ASSERT(fun->script() == script);
     unsigned nformal = fun->nargs;
 
     Value *firstUnused = args.end();
     JS_ASSERT(firstUnused == space().firstUnused());
 
     /* Include extra space to satisfy the method-jit stackLimit invariant. */
     unsigned nvals = VALUES_PER_STACK_FRAME + script->nslots + StackSpace::STACK_JIT_EXTRA;
@@ -441,20 +448,21 @@ ContextStack::getCallFrame(JSContext *cx
     return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
 }
 
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                               JSFunction &callee, JSScript *script,
                               InitialFrameFlags initial, MaybeReportError report)
 {
+    AssertCanGC();
     JS_ASSERT(onTop());
     JS_ASSERT(regs.sp == args.end());
     /* Cannot assert callee == args.callee() since this is called from LeaveTree. */
-    JS_ASSERT(script == callee.script());
+    JS_ASSERT(callee.script() == script);
 
     StackFrame::Flags flags = ToFrameFlags(initial);
     StackFrame *fp = getCallFrame(cx, report, args, &callee, script, &flags);
     if (!fp)
         return false;
 
     /* Initialize frame, locals, regs. */
     fp->initCallFrame(cx, callee, script, args.length(), flags);
@@ -467,27 +475,29 @@ ContextStack::pushInlineFrame(JSContext 
     return true;
 }
 
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                               JSFunction &callee, JSScript *script,
                               InitialFrameFlags initial, Value **stackLimit)
 {
+    AssertCanGC();
     if (!pushInlineFrame(cx, regs, args, callee, script, initial))
         return false;
     *stackLimit = space().conservativeEnd_;
     return true;
 }
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getFixupFrame(JSContext *cx, MaybeReportError report,
                             const CallArgs &args, JSFunction *fun, JSScript *script,
                             void *ncode, InitialFrameFlags initial, Value **stackLimit)
 {
+    AssertCanGC();
     JS_ASSERT(onTop());
     JS_ASSERT(fun->script() == args.callee().toFunction()->script());
     JS_ASSERT(fun->script() == script);
 
     StackFrame::Flags flags = ToFrameFlags(initial);
     StackFrame *fp = getCallFrame(cx, report, args, fun, script, &flags);
     if (!fp)
         return NULL;
@@ -521,16 +531,18 @@ ContextStack::popFrameAfterOverflow()
     StackFrame *fp = regs.fp();
     regs.popFrame(fp->actuals() + fp->numActualArgs());
 }
 
 inline JSScript *
 ContextStack::currentScript(jsbytecode **ppc,
                             MaybeAllowCrossCompartment allowCrossCompartment) const
 {
+    AutoAssertNoGC nogc;
+
     if (ppc)
         *ppc = NULL;
 
     if (!hasfp())
         return NULL;
 
     FrameRegs &regs = this->regs();
     StackFrame *fp = regs.fp();
@@ -546,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];
-        JSScript *script = frame->fun->script();
+        RawScript script = frame->fun->script();
         if (!allowCrossCompartment && script->compartment() != cx_->compartment)
             return NULL;
         if (ppc)
             *ppc = script->code + inlined->pcOffset;
         return script;
     }
 #endif
 
-    JSScript *script = fp->script();
+    RawScript script = fp->script();
     if (!allowCrossCompartment && script->compartment() != cx_->compartment)
         return NULL;
 
     if (ppc)
         *ppc = fp->pcQuadratic(*this);
     return script;
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -240,17 +240,17 @@ AssertDynamicScopeMatchesStaticScope(JSS
                 scope = &scope->asWith().enclosingScope();
 
             switch (i.type()) {
               case StaticScopeIter::BLOCK:
                 JS_ASSERT(i.block() == scope->asClonedBlock().staticBlock());
                 scope = &scope->asClonedBlock().enclosingScope();
                 break;
               case StaticScopeIter::FUNCTION:
-                JS_ASSERT(i.funScript() == scope->asCall().callee().script());
+                JS_ASSERT(scope->asCall().callee().script() == i.funScript());
                 scope = &scope->asCall().enclosingScope();
                 break;
               case StaticScopeIter::NAMED_LAMBDA:
                 scope = &scope->asDeclEnv().enclosingScope();
                 break;
             }
         }
     }
@@ -271,61 +271,64 @@ StackFrame::initCallObject(JSContext *cx
     pushOnScopeChain(*callobj);
     flags_ |= HAS_CALL_OBJ;
     return true;
 }
 
 bool
 StackFrame::prologue(JSContext *cx, bool newType)
 {
+    RootedScript script(cx, this->script());
+
     JS_ASSERT(!isGeneratorFrame());
-    JS_ASSERT(cx->regs().pc == script()->code);
+    JS_ASSERT(cx->regs().pc == script->code);
 
     if (isEvalFrame()) {
-        if (script()->strictModeCode) {
+        if (script->strictModeCode) {
             CallObject *callobj = CallObject::createForStrictEval(cx, this);
             if (!callobj)
                 return false;
             pushOnScopeChain(*callobj);
             flags_ |= HAS_CALL_OBJ;
         }
-        Probes::enterScript(cx, script(), NULL, this);
+        Probes::enterScript(cx, script, NULL, this);
         return true;
     }
 
     if (isGlobalFrame()) {
-        Probes::enterScript(cx, script(), NULL, this);
+        Probes::enterScript(cx, script, NULL, this);
         return true;
     }
 
     JS_ASSERT(isNonEvalFunctionFrame());
-    AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
+    AssertDynamicScopeMatchesStaticScope(script, scopeChain());
 
     if (fun()->isHeavyweight() && !initCallObject(cx))
         return false;
 
     if (isConstructing()) {
         RootedObject callee(cx, &this->callee());
         JSObject *obj = js_CreateThisForFunction(cx, callee, newType);
         if (!obj)
             return false;
         functionThis() = ObjectValue(*obj);
     }
 
-    Probes::enterScript(cx, script(), script()->function(), this);
+    Probes::enterScript(cx, script, script->function(), this);
     return true;
 }
 
 void
 StackFrame::epilogue(JSContext *cx)
 {
     JS_ASSERT(!isYielding());
     JS_ASSERT(!hasBlockChain());
 
-    Probes::exitScript(cx, script(), script()->function(), this);
+    RootedScript script(cx, this->script());
+    Probes::exitScript(cx, script, script->function(), this);
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().isForEval());
             if (cx->compartment->debugMode())
                 cx->runtime->debugScopes->onPopStrictEvalScope(this);
         } else if (isDirectEvalFrame()) {
             if (isDebuggerFrame())
@@ -351,19 +354,19 @@ StackFrame::epilogue(JSContext *cx)
     if (isGlobalFrame()) {
         JS_ASSERT(!scopeChain()->isScope());
         return;
     }
 
     JS_ASSERT(isNonEvalFunctionFrame());
 
     if (fun()->isHeavyweight())
-        JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().callee().script() == script());
+        JS_ASSERT_IF(hasCallObj(), scopeChain()->asCall().callee().script() == script);
     else
-        AssertDynamicScopeMatchesStaticScope(script(), scopeChain());
+        AssertDynamicScopeMatchesStaticScope(script, scopeChain());
 
     if (cx->compartment->debugMode())
         cx->runtime->debugScopes->onPopCall(this, cx);
 
 
     if (isConstructing() && returnValue().isPrimitive())
         setReturnValue(ObjectValue(constructorThis()));
 }
@@ -627,22 +630,23 @@ StackSpace::containingSegment(const Stac
     }
     JS_NOT_REACHED("frame not in stack space");
     return *(StackSegment *)NULL;
 }
 
 void
 StackSpace::markAndClobberFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
 {
+    AutoAssertNoGC nogc;
     Value *slotsBegin = fp->slots();
 
     /* If it's a scripted frame, we should have a pc. */
     JS_ASSERT(pc);
 
-    JSScript *script = fp->script();
+    RawScript script = fp->script();
     if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
         if (trc)
             gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
         return;
     }
 
     /*
      * If the JIT ran a lifetime analysis, then it may have left garbage in the
@@ -745,16 +749,17 @@ StackSpace::markActiveCompartments()
         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev())
             MarkCompartmentActive(fp);
     }
 }
 
 JS_FRIEND_API(bool)
 StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
 {
+    AssertCanGC();
     assertInvariants();
 
     JSCompartment *dest = cx->compartment;
     bool trusted = dest->principals == cx->runtime->trustedPrincipals();
     Value *end = trusted ? trustedEnd_ : defaultEnd_;
 
     /*
      * conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
@@ -924,16 +929,18 @@ ContextStack::onTop() const
  *
  * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
  * there is space for nvars slots on top of the stack.
  */
 Value *
 ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
                           MaybeExtend extend, bool *pushedSeg)
 {
+    AssertCanGC();
+
     Value *firstUnused = space().firstUnused();
     FrameRegs *regs = cx->maybeRegs();
 
 #ifdef JS_METHODJIT
     /*
      * The only calls made by inlined methodjit frames can be to other JIT
      * frames associated with the same VMFrame. If we try to Invoke(),
      * Execute() or so forth, any topmost inline frame will need to be
@@ -1002,16 +1009,17 @@ ContextStack::popSegment()
     if (!seg_)
         cx_->maybeMigrateVersionOverride();
 }
 
 bool
 ContextStack::pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *iag,
                              MaybeReportError report)
 {
+    AssertCanGC();
     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
 
     unsigned nvars = 2 + argc;
     Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &iag->pushedSeg_);
     if (!firstUnused)
         return false;
 
     MakeRangeGCSafe(firstUnused, nvars);
@@ -1040,20 +1048,21 @@ ContextStack::popInvokeArgs(const Invoke
     Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
 }
 
 StackFrame *
 ContextStack::pushInvokeFrame(JSContext *cx, MaybeReportError report,
                               const CallArgs &args, JSFunction *fun,
                               InitialFrameFlags initial, FrameGuard *fg)
 {
+    AssertCanGC();
     JS_ASSERT(onTop());
     JS_ASSERT(space().firstUnused() == args.end());
 
-    JSScript *script = fun->script();
+    RootedScript script(cx, fun->script());
 
     StackFrame::Flags flags = ToFrameFlags(initial);
     StackFrame *fp = getCallFrame(cx, report, args, fun, script, &flags);
     if (!fp)
         return NULL;
 
     fp->initCallFrame(cx, *fun, script, args.length(), flags);
     fg->regs_.prepareToRun(*fp, script);
@@ -1075,16 +1084,18 @@ ContextStack::pushInvokeFrame(JSContext 
     return true;
 }
 
 bool
 ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                                JSObject &scopeChain, ExecuteType type,
                                StackFrame *evalInFrame, ExecuteFrameGuard *efg)
 {
+    AssertCanGC();
+
     /*
      * Even though global code and indirect eval do not execute in the context
      * of the current frame, prev-link these to the current frame so that the
      * callstack looks right to the debugger (via CAN_EXTEND). This is safe
      * since the scope chain is what determines name lookup and access, not
      * prev-links.
      *
      * Eval-in-frame is the exception since it prev-links to an arbitrary frame
@@ -1176,16 +1187,17 @@ ContextStack::popFrame(const FrameGuard 
         popSegment();
 
     Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
 }
 
 bool
 ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
 {
+    AssertCanGC();
     HeapValue *genvp = gen->stackSnapshot;
     JS_ASSERT(genvp == HeapValueify(gen->fp->generatorArgsSnapshotBegin()));
     unsigned vplen = HeapValueify(gen->fp->generatorArgsSnapshotEnd()) - genvp;
 
     unsigned nvars = vplen + VALUES_PER_STACK_FRAME + gen->fp->script()->nslots;
     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
     if (!firstUnused)
         return false;
@@ -1245,16 +1257,18 @@ ContextStack::popGeneratorFrame(const Ge
 
     /* ~FrameGuard/popFrame will finish the popping. */
     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
 }
 
 bool
 ContextStack::saveFrameChain()
 {
+    AssertCanGC();
+
     bool pushedSeg;
     if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg))
         return false;
 
     JS_ASSERT(pushedSeg);
     JS_ASSERT(!hasfp());
     JS_ASSERT(onTop());
     JS_ASSERT(seg_->isEmpty());
@@ -1278,16 +1292,17 @@ StackIter::poisonRegs()
 {
     pc_ = (jsbytecode *)0xbad;
     script_ = (JSScript *)0xbad;
 }
 
 void
 StackIter::popFrame()
 {
+    AutoAssertNoGC nogc;
     StackFrame *oldfp = fp_;
     JS_ASSERT(seg_->contains(oldfp));
     fp_ = fp_->prev();
 
     if (seg_->contains(fp_)) {
         InlinedSite *inline_;
         pc_ = oldfp->prevpc(&inline_);
         JS_ASSERT(!inline_);
@@ -1305,16 +1320,17 @@ StackIter::popCall()
     calls_ = calls_->prev();
     if (!seg_->contains(fp_))
         poisonRegs();
 }
 
 void
 StackIter::settleOnNewSegment()
 {
+    AutoAssertNoGC nogc;
     if (FrameRegs *regs = seg_->maybeRegs()) {
         pc_ = regs->pc;
         if (fp_)
             script_ = fp_->script();
     } else {
         poisonRegs();
     }
 }
@@ -1345,16 +1361,18 @@ StackIter::startOnSegment(StackSegment *
  *    the innermost invocation so implicit memory ordering is used since both
  *    push values on the stack.
  *  - a native call's 'callee' argument is clobbered on return while the
  *    CallArgsList element is still visible.
  */
 void
 StackIter::settleOnNewState()
 {
+    AutoAssertNoGC nogc;
+
     /*
      * There are elements of the calls_ and fp_ chains that we want to skip
      * over so iterate until we settle on one or until there are no more.
      */
     while (true) {
         if (!fp_ && !calls_) {
             if (savedOption_ == GO_THROUGH_SAVED && seg_->prevInContext()) {
                 startOnSegment(seg_->prevInContext());
@@ -1489,16 +1507,17 @@ StackIter::StackIter(JSRuntime *rt, Stac
     startOnSegment(&seg);
     settleOnNewState();
 }
 
 #ifdef JS_ION
 void
 StackIter::popIonFrame()
 {
+    AutoAssertNoGC nogc;
     // Keep fp which describes all ion frames.
     poisonRegs();
     if (ionFrames_.isScripted() && ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         pc_ = ionInlineFrames_.pc();
         script_ = ionInlineFrames_.script();
     } else {
         ++ionFrames_;
@@ -1548,17 +1567,17 @@ StackIter::operator++()
         popFrame();
         settleOnNewState();
         break;
       case NATIVE:
         popCall();
         settleOnNewState();
         break;
       case ION:
-#ifdef JS_ION      
+#ifdef JS_ION
         popIonFrame();
         break;
 #else
         JS_NOT_REACHED("Unexpected state");
 #endif
     }
     return *this;
 }
@@ -1598,17 +1617,17 @@ bool
 StackIter::isFunctionFrame() const
 {
     switch (state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isFunctionFrame();
       case ION:
-#ifdef  JS_ION    
+#ifdef  JS_ION
         return ionInlineFrames_.isFunctionFrame();
 #else
         break;
 #endif
       case NATIVE:
         return false;
     }
     JS_NOT_REACHED("Unexpected state");
@@ -1650,17 +1669,17 @@ StackIter::isNonEvalFunctionFrame() cons
 
 bool
 StackIter::isConstructing() const
 {
     switch (state_) {
       case DONE:
         break;
       case ION:
-#ifdef JS_ION      
+#ifdef JS_ION
         return ionInlineFrames_.isConstructing();
 #else
         break;
 #endif        
       case SCRIPTED:
       case NATIVE:
         return fp_->isConstructing();
     }
@@ -1680,17 +1699,17 @@ StackIter::callee() const
       case ION:
 #ifdef JS_ION
         if (ionFrames_.isScripted())
             return ionInlineFrames_.callee();
         JS_ASSERT(ionFrames_.isNative());
         return ionFrames_.callee();
 #else
         break;
-#endif        
+#endif
       case NATIVE:
         return nativeArgs().callee().toFunction();
     }
     JS_NOT_REACHED("Unexpected state");
     return NULL;
 }
 
 Value
@@ -1786,16 +1805,17 @@ StackIter::thisv() const
     }
     JS_NOT_REACHED("Unexpected state");
     return NullValue();
 }
 
 size_t
 StackIter::numFrameSlots() const
 {
+    AutoAssertNoGC nogc;
     switch (state_) {
       case DONE:
       case NATIVE:
         break;
       case ION:
 #ifdef JS_ION
         return ionInlineFrames_.snapshotIterator().slots() - ionInlineFrames_.script()->nfixed;
 #else
@@ -1808,16 +1828,17 @@ StackIter::numFrameSlots() const
     }
     JS_NOT_REACHED("Unexpected state");
     return 0;
 }
 
 Value
 StackIter::frameSlotValue(size_t index) const
 {
+    AutoAssertNoGC nogc;
     switch (state_) {
       case DONE:
       case NATIVE:
         break;
       case ION:
 #ifdef JS_ION
       {
         ion::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -576,17 +576,17 @@ class StackFrame
         JS_ASSERT(hasBlockChain());
         return *blockChain_;
     }
 
     bool pushBlock(JSContext *cx, StaticBlockObject &block);
     void popBlock(JSContext *cx);
 
     /*
-     * With 
+     * With
      *
      * Entering/leaving a with (or E4X filter) block pushes/pops an object 
      * on the scope chain. Pushing uses pushOnScopeChain, popping should use
      * popWith.
      */
 
     void popWith(JSContext *cx);
 
@@ -600,22 +600,22 @@ class StackFrame
      * point of the outermost call. Inlined frame invariants:
      *
      * - Inlined frames have the same scope chain as the outer frame.
      * - Inlined frames have the same strictness as the outer frame.
      * - Inlined frames can only make calls to other JIT frames associated with
      *   the same VMFrame. Other calls force expansion of the inlined frames.
      */
 
-    HandleScript script() const {
+    js::Return<JSScript*> script() const {
         return isFunctionFrame()
                ? isEvalFrame()
-                 ? HandleScript::fromMarkedLocation(&u.evalScript)
-                 : fun()->script()
-               : HandleScript::fromMarkedLocation(&exec.script);
+                 ? u.evalScript
+                 : (JSScript*)fun()->script().unsafeGet()
+               : exec.script;
     }
 
     /*
      * Get the frame's current bytecode, assuming 'this' is in 'stack'. Beware,
      * as the name implies, pcQuadratic can lead to quadratic behavior in loops
      * such as:
      *
      *   for ( ...; fp; fp = fp->prev())
@@ -1190,17 +1190,18 @@ class FrameRegs
     void prepareToRun(StackFrame &fp, JSScript *script) {
         pc = script->code;
         sp = fp.slots() + script->nfixed;
         fp_ = &fp;
         inlined_ = NULL;
     }
 
     void setToEndOfScript() {
-        JSScript *script = fp()->script();
+        AutoAssertNoGC nogc;
+        RawScript script = fp()->script();
         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;
@@ -1784,17 +1785,17 @@ class StackIter
      * When entering IonMonkey, the top interpreter frame (pushed by the caller)
      * is kept on the stack as bookkeeping (with runningInIon() set). The
      * contents of the frame are ignored by Ion code (and GC) and thus
      * immediately become garbage and must not be touched directly.
      */
     StackFrame *interpFrame() const { JS_ASSERT(isScript() && !isIon()); return fp_; }
 
     jsbytecode *pc() const { JS_ASSERT(isScript()); return pc_; }
-    JSScript   *script() const { JS_ASSERT(isScript()); return script_; }
+    js::Return<JSScript*> script() const { JS_ASSERT(isScript()); return script_; }
     JSFunction *callee() const;
     Value       calleev() const;
     unsigned    numActualArgs() const;
 
     JSObject   *scopeChain() const;
 
     // Ensure that thisv is correct, see ComputeThis.
     bool        computeThis() const;
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -336,16 +336,17 @@ js::StaticStrings::getInt(int32_t i)
 {
     JS_ASSERT(hasInt(i));
     return getUint(uint32_t(i));
 }
 
 inline JSLinearString *
 js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
 {
+    AssertCanGC();
     JS_ASSERT(index < str->length());
     const jschar *chars = str->getChars(cx);
     if (!chars)
         return NULL;
     jschar c = chars[index];
     if (c < UNIT_STATIC_LIMIT)
         return getUnit(c);
     return js_NewDependentString(cx, str, index, 1);