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 110418 741fb7f8e5cb52247b69fb17b7214ede8c83d8eb
parent 110417 71eacb57041d465e7a32ce2bb2f554ef17b11d63
child 110419 0d27abf6448275d4ebada86d12934bffcc9013c9
push id23688
push userryanvm@gmail.com
push dateWed, 17 Oct 2012 01:52:19 +0000
treeherderautoland@dac5700acf8b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm, njn
bugs793577
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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);