Bug 816779 - Add some exact rooting assertions for JSScript; r=bhackett
authorTerrence Cole <terrence@mozilla.com>
Thu, 29 Nov 2012 10:22:12 -0800
changeset 116088 eb53ab86f515ba239e4ec604cfce3991c14154ef
parent 116087 0852e5efadac7f597cb772ceb9f8c2976377e3d1
child 116089 0723845d095e2cb3eb5ee968b45bce40cc00f1a7
push id24043
push userryanvm@gmail.com
push dateSat, 15 Dec 2012 21:18:16 +0000
treeherderautoland@c8a1314aa449 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs816779
milestone20.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 816779 - Add some exact rooting assertions for JSScript; r=bhackett
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/gc/Marking.cpp
js/src/gc/Root.h
js/src/ion/Bailouts.cpp
js/src/ion/C1Spewer.cpp
js/src/ion/C1Spewer.h
js/src/ion/CompileInfo.h
js/src/ion/ExecutionModeInlines.h
js/src/ion/Ion.cpp
js/src/ion/Ion.h
js/src/ion/IonCaches.cpp
js/src/ion/IonCaches.h
js/src/ion/IonCode.h
js/src/ion/IonCompartment.h
js/src/ion/IonFrameIterator.h
js/src/ion/IonFrames.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/IonSpewer.cpp
js/src/ion/IonSpewer.h
js/src/ion/JSONSpewer.cpp
js/src/ion/JSONSpewer.h
js/src/ion/LIR-Common.h
js/src/ion/MIR.h
js/src/ion/MIRGraph.h
js/src/ion/PcScriptCache.h
js/src/ion/SnapshotWriter.h
js/src/ion/Snapshots.cpp
js/src/ion/TypeOracle.cpp
js/src/ion/TypeOracle.h
js/src/ion/VMFunctions.cpp
js/src/ion/VMFunctions.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/shared/CodeGenerator-shared.h
js/src/ion/x86/CodeGenerator-x86.cpp
js/src/jsalloc.h
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsprobes.cpp
js/src/jsprobes.h
js/src/jspropertycache.cpp
js/src/jsprvtd.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/vm/ForkJoin.h
js/src/vm/GlobalObject.cpp
js/src/vm/ObjectImpl.cpp
js/src/vm/SPSProfiler.cpp
js/src/vm/SPSProfiler.h
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/Xdr.cpp
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -30,17 +30,17 @@ AssertInnerizedScopeChain(JSContext *cx,
             Rooted<JSObject*> obj(cx, o);
             JS_ASSERT(op(cx, obj) == o);
         }
     }
 #endif
 }
 
 static bool
-IsEvalCacheCandidate(JSScript *script)
+IsEvalCacheCandidate(UnrootedScript script)
 {
     // Make sure there are no inner objects which might use the wrong parent
     // and/or call scope by reusing the previous eval's script. Skip the
     // script's first object, which entrains the eval's scope.
     return script->savedCallerFun &&
            !script->hasSingletons &&
            script->objects()->length == 1 &&
            !script->hasRegexps();
@@ -52,17 +52,17 @@ EvalCacheHashPolicy::hash(const EvalCach
     return AddToHash(HashString(l.str->chars(), l.str->length()),
                      l.caller,
                      l.staticLevel,
                      l.version,
                      l.compartment);
 }
 
 /* static */ bool
-EvalCacheHashPolicy::match(JSScript *script, const EvalCacheLookup &l)
+EvalCacheHashPolicy::match(UnrootedScript script, const EvalCacheLookup &l)
 {
     JS_ASSERT(IsEvalCacheCandidate(script));
 
     // Get the source string passed for safekeeping in the atom map
     // by the prior eval to frontend::CompileScript.
     JSAtom *keyStr = script->atoms[0];
 
     return EqualStrings(keyStr, l.str) &&
@@ -116,23 +116,23 @@ class EvalScriptGuard
         lookup_.caller = caller;
         lookup_.staticLevel = staticLevel;
         lookup_.version = cx_->findVersion();
         lookup_.compartment = cx_->compartment;
         p_ = cx_->runtime->evalCache.lookupForAdd(lookup_);
         if (p_) {
             script_ = *p_;
             cx_->runtime->evalCache.remove(p_);
-            js_CallNewScriptHook(cx_, script_, NULL);
+            CallNewScriptHook(cx_, script_, NullPtr());
             script_->isCachedEval = false;
             script_->isActiveEval = true;
         }
     }
 
-    void setNewScript(JSScript *script) {
+    void setNewScript(UnrootedScript script) {
         // JSScript::initFromEmitter has already called js_CallNewScriptHook.
         JS_ASSERT(!script_ && script);
         script_ = script;
         script_->isActiveEval = true;
     }
 
     bool foundScript() {
         return !!script_;
@@ -269,18 +269,18 @@ EvalKernel(JSContext *cx, const CallArgs
                                                             : NOT_CALLED_FROM_JSOP_EVAL);
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, lineno)
                .setCompileAndGo(true)
                .setNoScriptRval(false)
                .setPrincipals(principals)
                .setOriginPrincipals(originPrincipals);
-        JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, options,
-                                                     chars, length, stableStr, staticLevel);
+        UnrootedScript compiled = frontend::CompileScript(cx, scopeobj, caller, options,
+                                                          chars, length, stableStr, staticLevel);
         if (!compiled)
             return false;
 
         esg.setNewScript(compiled);
     }
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
                          NULL /* evalInFrame */, args.rval().address());
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -32,26 +32,26 @@ CheckLength(JSContext *cx, size_t length
     if (length > UINT32_MAX) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SOURCE_TOO_LONG);
         return false;
     }
     return true;
 }
 
 static bool
-SetSourceMap(JSContext *cx, TokenStream &tokenStream, ScriptSource *ss, JSScript *script)
+SetSourceMap(JSContext *cx, TokenStream &tokenStream, ScriptSource *ss, UnrootedScript script)
 {
     if (tokenStream.hasSourceMap()) {
         if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename))
             return false;
     }
     return true;
 }
 
-JSScript *
+UnrootedScript
 frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
                         const CompileOptions &options,
                         StableCharPtr chars, size_t length,
                         JSString *source_ /* = NULL */,
                         unsigned staticLevel /* = 0 */)
 {
     RootedString source(cx, source_);
 
@@ -71,94 +71,94 @@ frontend::CompileScript(JSContext *cx, H
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(callerFrame, options.compileAndGo);
     JS_ASSERT_IF(staticLevel != 0, callerFrame);
 
     if (!CheckLength(cx, length))
-        return NULL;
+        return UnrootedScript(NULL);
     JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
     ScriptSource *ss = cx->new_<ScriptSource>();
     if (!ss)
-        return NULL;
+        return UnrootedScript(NULL);
     ScriptSourceHolder ssh(cx->runtime, ss);
     SourceCompressionToken sct(cx);
     switch (options.sourcePolicy) {
       case CompileOptions::SAVE_SOURCE:
         if (!ss->setSourceCopy(cx, chars, length, false, &sct))
-            return NULL;
+            return UnrootedScript(NULL);
         break;
       case CompileOptions::LAZY_SOURCE:
         ss->setSourceRetrievable();
         break;
       case CompileOptions::NO_SOURCE:
         break;
     }
 
     Parser parser(cx, options, chars, length, /* foldConstants = */ true);
     if (!parser.init())
-        return NULL;
+        return UnrootedScript(NULL);
     parser.sct = &sct;
 
     GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
 
     ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0);
     if (!pc.init())
-        return NULL;
+        return UnrootedScript(NULL);
 
     bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame();
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
                                                   options, staticLevel, ss, 0, length));
     if (!script)
-        return NULL;
+        return UnrootedScript(NULL);
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).
     InternalHandle<Bindings*> bindings(script, &script->bindings);
     if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL))
-        return NULL;
+        return UnrootedScript(NULL);
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL;
     JS_ASSERT_IF(globalScope, globalScope->isNative());
     JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
 
     BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope,
                         options.lineno, options.selfHostingMode);
     if (!bce.init())
-        return NULL;
+        return UnrootedScript(NULL);
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
     if (callerFrame && callerFrame->script()->strict)
         globalsc.strict = true;
 
     if (options.compileAndGo) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
             JSAtom *atom = AtomizeString(cx, source);
             jsatomid _;
             if (!atom || !bce.makeAtomIndex(atom, &_))
-                return NULL;
+                return UnrootedScript(NULL);
         }
 
         if (callerFrame && callerFrame->isFunctionFrame()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
              * function captured in case it refers to an upvar, and someone
              * wishes to decompile it while it's running.
              */
             JSFunction *fun = callerFrame->fun();
             ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
             if (!funbox)
-                return NULL;
+                return UnrootedScript(NULL);
             bce.objectList.add(funbox);
         }
     }
 
     ParseNode *pn;
 #if JS_HAS_XML_SUPPORT
     pn = NULL;
     bool onlyXML;
@@ -168,84 +168,84 @@ frontend::CompileScript(JSContext *cx, H
     TokenStream &tokenStream = parser.tokenStream;
     bool canHaveDirectives = true;
     for (;;) {
         TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
         if (tt <= TOK_EOF) {
             if (tt == TOK_EOF)
                 break;
             JS_ASSERT(tt == TOK_ERROR);
-            return NULL;
+            return UnrootedScript(NULL);
         }
 
         pn = parser.statement();
         if (!pn)
-            return NULL;
+            return UnrootedScript(NULL);
 
         if (canHaveDirectives) {
             if (!parser.maybeParseDirective(pn, &canHaveDirectives))
-                return NULL;
+                return UnrootedScript(NULL);
         }
 
         if (!FoldConstants(cx, pn, &parser))
-            return NULL;
+            return UnrootedScript(NULL);
         if (!NameFunctions(cx, pn))
-            return NULL;
+            return UnrootedScript(NULL);
 
         if (!EmitTree(cx, &bce, pn))
-            return NULL;
+            return UnrootedScript(NULL);
 
 #if JS_HAS_XML_SUPPORT
         if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem())
             onlyXML = false;
 #endif
         parser.freeTree(pn);
     }
 
     if (!SetSourceMap(cx, tokenStream, ss, script))
-        return NULL;
+        return UnrootedScript(NULL);
 
 #if JS_HAS_XML_SUPPORT
     /*
      * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
      * For background, see:
      *
      * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
      */
     if (pn && onlyXML && !callerFrame) {
         parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM);
-        return NULL;
+        return UnrootedScript(NULL);
     }
 #endif
 
     // It's an error to use |arguments| in a function that has a rest parameter.
     if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) {
         HandlePropertyName arguments = cx->names().arguments;
         for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
             if (r.front().key() == arguments) {
                 parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
-                return NULL;
+                return UnrootedScript(NULL);
             }
         }
     }
 
     /*
      * Nowadays the threaded interpreter needs a stop instruction, so we
      * do have to emit that here.
      */
     if (Emit1(cx, &bce, JSOP_STOP) < 0)
-        return NULL;
+        return UnrootedScript(NULL);
 
     if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
-        return NULL;
+        return UnrootedScript(NULL);
 
     bce.tellDebuggerAboutCompiledScript(cx);
 
     if (!sct.complete())
-        return NULL;
+        return UnrootedScript(NULL);
 
     return script;
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 bool
 frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -8,17 +8,17 @@
 #ifndef BytecodeCompiler_h__
 #define BytecodeCompiler_h__
 
 #include "frontend/Parser.h"
 
 namespace js {
 namespace frontend {
 
-JSScript *
+UnrootedScript
 CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
               const CompileOptions &options, StableCharPtr chars, size_t length,
               JSString *source_ = NULL, unsigned staticLevel = 0);
 
 bool
 CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
                     const AutoNameVector &formals, StableCharPtr chars, size_t length);
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1683,17 +1683,18 @@ BytecodeEmitter::needsImplicitThis()
             return true;
     }
     return false;
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext *cx)
 {
-    js_CallNewScriptHook(cx, script, script->function());
+    RootedFunction function(cx, script->function());
+    CallNewScriptHook(cx, script, function);
     if (!parent) {
         GlobalObject *compileAndGoGlobal = NULL;
         if (script->compileAndGo)
             compileAndGoGlobal = &script->global();
         Debugger::onNewScript(cx, script, compileAndGoGlobal);
     }
 }
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -63,32 +63,32 @@ PushMarkStack(GCMarker *gcmarker, JSXML 
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSObject *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, JSScript *thing);
+PushMarkStack(GCMarker *gcmarker, UnrootedScript thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, UnrootedShape thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 namespace js {
 namespace gc {
 
 static void MarkChildren(JSTracer *trc, JSString *str);
-static void MarkChildren(JSTracer *trc, JSScript *script);
+static void MarkChildren(JSTracer *trc, UnrootedScript script);
 static void MarkChildren(JSTracer *trc, UnrootedShape shape);
 static void MarkChildren(JSTracer *trc, UnrootedBaseShape base);
 static void MarkChildren(JSTracer *trc, types::TypeObject *type);
 static void MarkChildren(JSTracer *trc, ion::IonCode *code);
 #if JS_HAS_XML_SUPPORT
 static void MarkChildren(JSTracer *trc, JSXML *xml);
 #endif
 
@@ -641,17 +641,17 @@ gc::MarkCrossCompartmentSlot(JSTracer *t
 {
     if (dst->isMarkable() && ShouldMarkCrossCompartment(trc, src, (Cell *)dst->toGCThing()))
         MarkSlot(trc, dst, name);
 }
 
 /*** Special Marking ***/
 
 void
-gc::MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name)
+gc::MarkObject(JSTracer *trc, HeapPtr<GlobalObject, RawScript> *thingp, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkInternal(trc, thingp->unsafeGet());
 }
 
 void
 gc::MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
 {
@@ -714,17 +714,17 @@ PushMarkStack(GCMarker *gcmarker, types:
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushType(thing);
 }
 
 static void
-PushMarkStack(GCMarker *gcmarker, JSScript *thing)
+PushMarkStack(GCMarker *gcmarker, UnrootedScript thing)
 {
     JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
 
     /*
      * We mark scripts directly rather than pushing on the stack as they can
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
@@ -921,17 +921,17 @@ gc::MarkChildren(JSTracer *trc, JSString
 {
     if (str->hasBase())
         str->markBase(trc);
     else if (str->isRope())
         str->asRope().markChildren(trc);
 }
 
 static void
-gc::MarkChildren(JSTracer *trc, JSScript *script)
+gc::MarkChildren(JSTracer *trc, UnrootedScript script)
 {
     script->markChildren(trc);
 }
 
 static void
 gc::MarkChildren(JSTracer *trc, UnrootedShape shape)
 {
     shape->markChildren(trc);
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -8,16 +8,17 @@
 #ifndef jsgc_root_h__
 #define jsgc_root_h__
 
 #ifdef __cplusplus
 
 #include "mozilla/TypeTraits.h"
 #include "mozilla/GuardObjects.h"
 
+#include "js/Utility.h"
 #include "js/TemplateLib.h"
 
 #include "jspubtd.h"
 
 /*
  * Moving GC Stack Rooting
  *
  * A moving GC may change the physical location of GC allocated things, even
@@ -238,16 +239,18 @@ class Handle : public js::HandleBase<T>
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     const T *address() const { return ptr; }
     T get() const { return *ptr; }
 
     operator T() const { return get(); }
     T operator->() const { return get(); }
 
+    bool operator!=(const T &other) { return *ptr != other; }
+
   private:
     Handle() {}
 
     const T *ptr;
 
     template <typename S>
     void operator=(S v) MOZ_DELETE;
 };
@@ -333,17 +336,17 @@ namespace js {
  * Raw pointer used as documentation that a parameter does not need to be
  * rooted.
  */
 typedef JSObject *                  RawObject;
 typedef JSFunction *                RawFunction;
 typedef JSScript *                  RawScript;
 typedef JSString *                  RawString;
 typedef jsid                        RawId;
-typedef Value                       RawValue;
+typedef JS::Value                   RawValue;
 
 /*
  * InternalHandle is a handle to an internal pointer into a gcthing. Use
  * InternalHandle when you have a pointer to a direct field of a gcthing, or
  * when you need a parameter type for something that *may* be a pointer to a
  * direct field of a gcthing.
  */
 template <typename T>
@@ -388,17 +391,17 @@ class InternalHandle<T*>
      * gcthing, and so does not need to be rooted in the first place. Use these
      * InternalHandles to pass pointers into functions that also need to accept
      * regular InternalHandles to gcthing fields.
      *
      * Make this private to prevent accidental misuse; this is only for
      * fromMarkedLocation().
      */
     InternalHandle(T *field)
-      : holder(reinterpret_cast<void * const *>(&NullPtr::constNullValue)),
+      : holder(reinterpret_cast<void * const *>(&JS::NullPtr::constNullValue)),
         offset(uintptr_t(field))
     {}
 };
 
 #ifdef DEBUG
 /*
  * |Unrooted<T>| acts as an AutoAssertNoGC after it is initialized. It otherwise
  * acts like as a normal pointer of type T.
@@ -423,72 +426,72 @@ class Unrooted
                     typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
     template <typename S>
     Unrooted(const JS::Handle<S> &root,
              typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
       : ptr_(root.get())
     {
         JS_ASSERT(ptr_ != UninitializedTag());
-        EnterAssertNoGCScope();
+        JS::EnterAssertNoGCScope();
     }
 
     /*
      * |Unrooted<T>| can initialize by copying from a convertible type
      * |Unrooted<S>|. This enables usage such as:
      *
      * Unrooted<BaseShape*> base = js_NewBaseShape(cx);
      * Unrooted<UnownedBaseShape*> ubase = static_cast<UnrootedUnownedBaseShape>(ubase);
      */
     template <typename S>
     Unrooted(const Unrooted<S> &other)
         /* Note: |static_cast<S>| acquires other.ptr_ in DEBUG builds. */
       : ptr_(static_cast<T>(static_cast<S>(other)))
     {
         if (ptr_ != UninitializedTag())
-            EnterAssertNoGCScope();
+            JS::EnterAssertNoGCScope();
     }
 
     Unrooted(const Unrooted &other) : ptr_(other.ptr_) {
         if (ptr_ != UninitializedTag())
-            EnterAssertNoGCScope();
+            JS::EnterAssertNoGCScope();
     }
 
     Unrooted(const T &p) : ptr_(p) {
         JS_ASSERT(ptr_ != UninitializedTag());
-        EnterAssertNoGCScope();
+        JS::EnterAssertNoGCScope();
     }
 
     Unrooted(const JS::NullPtr &) : ptr_(NULL) {
-        EnterAssertNoGCScope();
+        JS::EnterAssertNoGCScope();
     }
 
     ~Unrooted() {
         if (ptr_ != UninitializedTag())
-            LeaveAssertNoGCScope();
+            JS::LeaveAssertNoGCScope();
     }
 
     void drop() {
         if (ptr_ != UninitializedTag())
-            LeaveAssertNoGCScope();
+            JS::LeaveAssertNoGCScope();
         ptr_ = UninitializedTag();
     }
 
     /* See notes for Unrooted::Unrooted(const T &) */
     Unrooted &operator=(T other) {
         JS_ASSERT(other != UninitializedTag());
         if (ptr_ == UninitializedTag())
-            EnterAssertNoGCScope();
+            JS::EnterAssertNoGCScope();
         ptr_ = other;
         return *this;
     }
     Unrooted &operator=(Unrooted other) {
         JS_ASSERT(other.ptr_ != UninitializedTag());
         if (ptr_ == UninitializedTag())
-            EnterAssertNoGCScope();
+            JS::EnterAssertNoGCScope();
         ptr_ = other.ptr_;
         return *this;
     }
 
     operator T() const { return (ptr_ == UninitializedTag()) ? NULL : ptr_; }
     T *operator&() { return &ptr_; }
     const T operator->() const { JS_ASSERT(ptr_ != UninitializedTag()); return ptr_; }
     bool operator==(const T &other) { return ptr_ == other; }
@@ -766,26 +769,26 @@ class Rooted<JSStableString *>;
 #ifdef DEBUG
 template <typename T> template <typename S>
 inline
 Unrooted<T>::Unrooted(const Rooted<S> &root,
                       typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy)
   : ptr_(root.get())
 {
     JS_ASSERT(ptr_ != UninitializedTag());
-    EnterAssertNoGCScope();
+    JS::EnterAssertNoGCScope();
 }
 #endif /* DEBUG */
 
 typedef Rooted<JSObject*>    RootedObject;
 typedef Rooted<JSFunction*>  RootedFunction;
 typedef Rooted<JSScript*>    RootedScript;
 typedef Rooted<JSString*>    RootedString;
 typedef Rooted<jsid>         RootedId;
-typedef Rooted<Value>        RootedValue;
+typedef Rooted<JS::Value>    RootedValue;
 
 /*
  * Mark a stack location as a root for the rooting analysis, without actually
  * rooting it in release builds. This should only be used for stack locations
  * of GC things that cannot be relocated by a garbage collection, and that
  * are definitely reachable via another path.
  */
 class SkipRoot
@@ -920,17 +923,17 @@ JS_FRIEND_API(bool) NeedRelaxedRootCheck
 namespace js {
 
 /*
  * Hook for dynamic root analysis. Checks the native stack and poisons
  * references to GC things which have not been rooted.
  */
 inline void MaybeCheckStackRoots(JSContext *cx, bool relax = true)
 {
-    AssertCanGC();
+    JS::AssertCanGC();
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
     if (relax && NeedRelaxedRootChecks())
         return;
     CheckStackRoots(cx);
 #endif
 }
 
 namespace gc {
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -531,17 +531,17 @@ ion::EnsureHasScopeObjects(JSContext *cx
     }
     return true;
 }
 
 uint32_t
 ion::BoundsCheckFailure()
 {
     JSContext *cx = GetIonContext()->cx;
-    JSScript *script = GetBailedJSScript(cx);
+    UnrootedScript script = GetBailedJSScript(cx);
 
     IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename,
             script->lineno);
 
     if (!script->failedBoundsCheck) {
         script->failedBoundsCheck = true;
 
         // Invalidate the script to force a recompile.
@@ -552,33 +552,33 @@ ion::BoundsCheckFailure()
 
     return true;
 }
 
 uint32_t
 ion::ShapeGuardFailure()
 {
     JSContext *cx = GetIonContext()->cx;
-    JSScript *script = GetBailedJSScript(cx);
+    UnrootedScript script = GetBailedJSScript(cx);
 
     JS_ASSERT(script->hasIonScript());
     JS_ASSERT(!script->ion->invalidated());
 
     script->failedShapeGuard = true;
 
     IonSpew(IonSpew_Invalidate, "Invalidating due to shape guard failure");
 
     return Invalidate(cx, script);
 }
 
 uint32_t
 ion::CachedShapeGuardFailure()
 {
     JSContext *cx = GetIonContext()->cx;
-    JSScript *script = GetBailedJSScript(cx);
+    UnrootedScript script = GetBailedJSScript(cx);
 
     JS_ASSERT(script->hasIonScript());
     JS_ASSERT(!script->ion->invalidated());
 
     script->failedShapeGuard = true;
 
     // Purge JM caches in the script and all inlined script, to avoid baking in
     // the same shape guard next time.
@@ -617,17 +617,17 @@ ion::ThunkToInterpreter(Value *vp)
     // in order to create an arguments object for them.  However, there is an
     // invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the
     // prologue), so we must create one now for each inlined frame which needs
     // one.
     {
         br->entryfp()->clearRunningInIon();
         ScriptFrameIter iter(cx);
         StackFrame *fp = NULL;
-        Rooted<JSScript*> script(cx, NULL);
+        Rooted<JSScript*> script(cx);
         do {
             fp = iter.interpFrame();
             script = iter.script();
             if (script->needsArgsObj()) {
                 // Currently IonMonkey does not compile if the script needs an
                 // arguments object, so the frame should not have any argument
                 // object yet.
                 JS_ASSERT(!fp->hasArgsObj());
--- a/js/src/ion/C1Spewer.cpp
+++ b/js/src/ion/C1Spewer.cpp
@@ -22,17 +22,17 @@ using namespace js::ion;
 bool
 C1Spewer::init(const char *path)
 {
     spewout_ = fopen(path, "w");
     return (spewout_ != NULL);
 }
 
 void
-C1Spewer::beginFunction(MIRGraph *graph, JSScript *script)
+C1Spewer::beginFunction(MIRGraph *graph, HandleScript script)
 {
     if (!spewout_)
         return;
 
     this->graph  = graph;
     this->script = script;
 
     fprintf(spewout_, "begin_compilation\n");
@@ -73,17 +73,16 @@ C1Spewer::spewIntervals(const char *pass
 
     fprintf(spewout_, "end_intervals\n");
     fflush(spewout_);
 }
 
 void
 C1Spewer::endFunction()
 {
-    return;
 }
 
 void
 C1Spewer::finish()
 {
     if (spewout_)
         fclose(spewout_);
 }
--- a/js/src/ion/C1Spewer.h
+++ b/js/src/ion/C1Spewer.h
@@ -5,39 +5,42 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef DEBUG
 
 #ifndef jsion_c1spewer_h__
 #define jsion_c1spewer_h__
 
+#include "gc/Root.h"
+#include "jsscript.h"
+
 namespace js {
 namespace ion {
 
 class MDefinition;
 class MInstruction;
 class MBasicBlock;
 class MIRGraph;
 class LinearScanAllocator;
 class LInstruction;
 
 class C1Spewer
 {
     MIRGraph *graph;
-    JSScript *script;
+    HandleScript script;
     FILE *spewout_;
 
   public:
     C1Spewer()
-      : graph(NULL), script(NULL), spewout_(NULL)
+      : graph(NULL), script(NullPtr()), spewout_(NULL)
     { }
 
     bool init(const char *path);
-    void beginFunction(MIRGraph *graph, JSScript *script);
+    void beginFunction(MIRGraph *graph, HandleScript script);
     void spewPass(const char *pass);
     void spewIntervals(const char *pass, LinearScanAllocator *regalloc);
     void endFunction();
     void finish();
 
   private:
     void spewPass(FILE *fp, MBasicBlock *block);
     void spewIntervals(FILE *fp, LinearScanAllocator *regalloc, LInstruction *ins, size_t &nextId);
--- a/js/src/ion/CompileInfo.h
+++ b/js/src/ion/CompileInfo.h
@@ -25,26 +25,26 @@ enum ExecutionMode {
     // e.g. by ParallelArray
     ParallelExecution
 };
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
-    CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
+    CompileInfo(UnrootedScript script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
                 ExecutionMode executionMode)
       : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
         executionMode_(executionMode)
     {
         JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
         nslots_ = script->nslots + CountArgSlots(fun);
     }
 
-    JSScript *script() const {
+    UnrootedScript script() const {
         return script_;
     }
     JSFunction *fun() const {
         return fun_;
     }
     bool constructing() const {
         return constructing_;
     }
--- a/js/src/ion/ExecutionModeInlines.h
+++ b/js/src/ion/ExecutionModeInlines.h
@@ -6,37 +6,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsion_compilemode_h__
 #define jsion_compilemode_h__
 
 namespace js {
 namespace ion {
 
-static inline bool HasIonScript(JSScript *script, ExecutionMode cmode)
+static inline bool HasIonScript(UnrootedScript script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->hasIonScript();
       case ParallelExecution: return script->hasParallelIonScript();
     }
     JS_NOT_REACHED("No such execution mode");
     return false;
 }
 
-static inline IonScript *GetIonScript(JSScript *script, ExecutionMode cmode)
+static inline IonScript *GetIonScript(UnrootedScript script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->ion;
       case ParallelExecution: return script->parallelIon;
     }
     JS_NOT_REACHED("No such execution mode");
     return NULL;
 }
 
-static inline void SetIonScript(JSScript *script, ExecutionMode cmode, IonScript *ionScript)
+static inline void SetIonScript(UnrootedScript script, ExecutionMode cmode, IonScript *ionScript)
 {
     switch (cmode) {
       case SequentialExecution: script->ion = ionScript; return;
       case ParallelExecution: script->parallelIon = ionScript; return;
     }
     JS_NOT_REACHED("No such execution mode");
 }
 
@@ -53,17 +53,17 @@ static inline bool CanIonCompile(HandleS
 static inline bool CanIonCompile(JSContext *cx, HandleFunction fun, ExecutionMode cmode)
 {
     if (!fun->isInterpreted())
         return false;
     RootedScript script(cx, fun->nonLazyScript());
     return CanIonCompile(script, cmode);
 }
 
-static inline bool CompilingOffThread(JSScript *script, ExecutionMode cmode)
+static inline bool CompilingOffThread(UnrootedScript script, ExecutionMode cmode)
 {
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
     }
     JS_NOT_REACHED("No such execution mode");
     return false;
 }
@@ -73,17 +73,17 @@ static inline bool CompilingOffThread(Ha
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
     }
     JS_NOT_REACHED("No such execution mode");
     return false;
 }
 
-static inline bool Disabled(JSScript *script, ExecutionMode cmode) {
+static inline bool Disabled(UnrootedScript script, ExecutionMode cmode) {
     switch (cmode) {
       case SequentialExecution: return script->isIonCompilingOffThread();
       case ParallelExecution: return script->isParallelIonCompilingOffThread();
     }
     JS_NOT_REACHED("No such execution mode");
     return false;
 }
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -474,17 +474,17 @@ IonScript::New(JSContext *cx, uint32_t f
     size_t paddedBailoutSize = AlignBytes(bailoutEntries * sizeof(uint32_t), DataAlignment);
     size_t paddedConstantsSize = AlignBytes(constants * sizeof(Value), DataAlignment);
     size_t paddedSafepointIndicesSize = AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
     size_t paddedOsiIndicesSize = AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
     size_t paddedCacheEntriesSize = AlignBytes(cacheEntries * sizeof(IonCache), DataAlignment);
     size_t paddedPrebarrierEntriesSize =
         AlignBytes(prebarrierEntries * sizeof(CodeOffsetLabel), DataAlignment);
     size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
-    size_t paddedScriptSize = AlignBytes(scriptEntries * sizeof(JSScript *), DataAlignment);
+    size_t paddedScriptSize = AlignBytes(scriptEntries * sizeof(RawScript), DataAlignment);
     size_t bytes = paddedSnapshotsSize +
                    paddedBailoutSize +
                    paddedConstantsSize +
                    paddedSafepointIndicesSize+
                    paddedOsiIndicesSize +
                    paddedCacheEntriesSize +
                    paddedPrebarrierEntriesSize +
                    paddedSafepointSize +
@@ -765,17 +765,17 @@ IonScript::purgeCaches(JSCompartment *c)
 }
 
 void
 ion::ToggleBarriers(JSCompartment *comp, bool needs)
 {
     IonContext ictx(NULL, comp, NULL);
     AutoFlushCache afc("ToggleBarriers");
     for (gc::CellIterUnderGC i(comp, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
+        UnrootedScript script = i.get<JSScript>();
         if (script->hasIonScript())
             script->ion->toggleBarriers(needs);
     }
 }
 
 namespace js {
 namespace ion {
 
@@ -1117,17 +1117,17 @@ AttachFinishedCompilations(JSContext *cx
     compilations.clear();
 #endif
 }
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 template <typename CompileContext>
 static bool
-IonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
+IonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing,
            CompileContext &compileContext)
 {
     AssertCanGC();
 #if JS_TRACE_LOGGING
     AutoTraceLog logger(TraceLogging::defaultLogger(),
                         TraceLogging::ION_COMPILE_START,
                         TraceLogging::ION_COMPILE_STOP,
                         script);
@@ -1179,17 +1179,18 @@ IonCompile(JSContext *cx, JSScript *scri
 
 bool
 SequentialCompileContext::compile(IonBuilder *builder, MIRGraph *graph,
                                   AutoDestroyAllocator &autoDestroy)
 {
     JS_ASSERT(!builder->script()->ion);
     JSContext *cx = GetIonContext()->cx;
 
-    IonSpewNewFunction(graph, builder->script());
+    RootedScript builderScript(cx, builder->script());
+    IonSpewNewFunction(graph, builderScript);
 
     if (!builder->build()) {
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return false;
     }
     builder->clearForBackEnd();
 
     // Try to compile the script off thread, if possible. Compilation cannot be
@@ -1223,17 +1224,17 @@ SequentialCompileContext::compile(IonBui
     js_delete(codegen);
 
     IonSpewEndFunction();
 
     return success;
 }
 
 bool
-TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
+TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing)
 {
     SequentialCompileContext compileContext;
     if (!IonCompile(cx, script, fun, osrPc, constructing, compileContext)) {
         if (!cx->isExceptionPending())
             ForbidCompilation(cx, script);
         return false;
     }
     return true;
@@ -1275,34 +1276,34 @@ CheckFrame(StackFrame *fp)
         IonSpew(IonSpew_Abort, "too many actual args");
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckScript(JSScript *script)
+CheckScript(UnrootedScript script)
 {
     if (script->needsArgsObj()) {
         // Functions with arguments objects, are not supported yet.
         IonSpew(IonSpew_Abort, "script has argsobj");
         return false;
     }
 
     if (!script->compileAndGo) {
         IonSpew(IonSpew_Abort, "not compile-and-go");
         return false;
     }
 
     return true;
 }
 
 static bool
-CheckScriptSize(JSScript *script)
+CheckScriptSize(UnrootedScript script)
 {
     if (!js_IonOptions.limitScriptSize)
         return true;
 
     static const uint32_t MAX_SCRIPT_SIZE = 2000;
     static const uint32_t MAX_LOCALS_AND_ARGS = 256;
 
     if (script->length > MAX_SCRIPT_SIZE) {
@@ -1315,17 +1316,17 @@ CheckScriptSize(JSScript *script)
         IonSpew(IonSpew_Abort, "Too many locals and arguments (%u)", numLocalsAndArgs);
         return false;
     }
 
     return true;
 }
 
 static MethodStatus
-Compile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
+Compile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing)
 {
     JS_ASSERT(ion::IsEnabled(cx));
     JS_ASSERT_IF(osrPc != NULL, (JSOp)*osrPc == JSOP_LOOPENTRY);
 
     if (cx->compartment->debugMode()) {
         IonSpew(IonSpew_Abort, "debugging");
         return Method_CantCompile;
     }
@@ -1388,17 +1389,17 @@ ion::CanEnterAtBranch(JSContext *cx, Han
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL;
+    RootedFunction fun(cx, fp->isFunctionFrame() ? fp->fun() : NULL);
     MethodStatus status = Compile(cx, script, fun, pc, fp->isConstructing());
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->ion->osrPc() != pc)
@@ -1437,17 +1438,17 @@ ion::CanEnter(JSContext *cx, HandleScrip
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
         ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL;
+    RootedFunction fun(cx, fp->isFunctionFrame() ? fp->fun() : NULL);
     MethodStatus status = Compile(cx, script, fun, NULL, fp->isConstructing());
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     return Method_Compiled;
@@ -1702,17 +1703,17 @@ InvalidateActivation(FreeOp *fop, uint8_
           case IonFrame_Exit:
             IonSpew(IonSpew_Invalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case IonFrame_OptimizedJS:
           {
             JS_ASSERT(it.isScripted());
             IonSpew(IonSpew_Invalidate, "#%d JS frame @ %p, %s:%d (fun: %p, script: %p, pc %p)",
                     frameno, it.fp(), it.script()->filename, it.script()->lineno,
-                    it.maybeCallee(), it.script(), it.returnAddressToFp());
+                    it.maybeCallee(), (RawScript)it.script(), it.returnAddressToFp());
             break;
           }
           case IonFrame_Rectifier:
             IonSpew(IonSpew_Invalidate, "#%d rectifier frame @ %p", frameno, it.fp());
             break;
           case IonFrame_Bailed_JS:
             JS_NOT_REACHED("invalid");
             break;
@@ -1876,17 +1877,17 @@ ion::Invalidate(types::TypeCompartment &
             continue;
           case types::CompilerOutput::Ion:
             break;
           case types::CompilerOutput::ParallelIon:
             executionMode = ParallelExecution;
             break;
         }
         JS_ASSERT(co.isValid());
-        JSScript *script = co.script;
+        UnrootedScript script = co.script;
         IonScript *ionScript = GetIonScript(script, executionMode);
 
         JSCompartment *compartment = script->compartment();
         if (compartment->needsBarrier()) {
             // We're about to remove edges from the JSScript to gcthings
             // embedded in the IonScript. Perform one final trace of the
             // IonScript for the incremental GC, as it must know about
             // those edges.
@@ -1902,34 +1903,36 @@ ion::Invalidate(types::TypeCompartment &
         if (resetUses)
             script->resetUseCount();
     }
 }
 
 void
 ion::Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses)
 {
+    AutoAssertNoGC nogc;
     ion::Invalidate(cx->compartment->types, cx->runtime->defaultFreeOp(), invalid, resetUses);
 }
 
 bool
-ion::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
+ion::Invalidate(JSContext *cx, UnrootedScript script, bool resetUses)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(script->hasIonScript());
 
     Vector<types::RecompileInfo> scripts(cx);
     if (!scripts.append(script->ionScript()->recompileInfo()))
         return false;
 
     Invalidate(cx, scripts, resetUses);
     return true;
 }
 
 void
-ion::FinishInvalidation(FreeOp *fop, JSScript *script)
+ion::FinishInvalidation(FreeOp *fop, UnrootedScript script)
 {
     if (!script->hasIonScript())
         return;
 
     /*
      * If this script has Ion code on the stack, invalidation() will return
      * true. In this case we have to wait until destroying it.
      */
@@ -1952,17 +1955,17 @@ ion::MarkValueFromIon(JSRuntime *rt, Val
 
 void
 ion::MarkShapeFromIon(JSRuntime *rt, Shape **shapep)
 {
     gc::MarkShapeUnbarriered(&rt->gcMarker, shapep, "write barrier");
 }
 
 void
-ion::ForbidCompilation(JSContext *cx, JSScript *script)
+ion::ForbidCompilation(JSContext *cx, UnrootedScript script)
 {
     IonSpew(IonSpew_Abort, "Disabling Ion compilation of script %s:%d",
             script->filename, script->lineno);
 
     CancelOffThreadIonCompile(cx->compartment, script);
 
     if (script->hasIonScript()) {
         // It is only safe to modify script->ion if the script is not currently
@@ -1973,17 +1976,17 @@ ion::ForbidCompilation(JSContext *cx, JS
         if (!Invalidate(cx, script, false))
             return;
     }
 
     script->ion = ION_DISABLED_SCRIPT;
 }
 
 uint32_t
-ion::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
+ion::UsesBeforeIonRecompile(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(pc == script->code || JSOp(*pc) == JSOP_LOOPENTRY);
 
     uint32_t minUses = js_IonOptions.usesBeforeCompile;
     if (JSOp(*pc) != JSOP_LOOPENTRY || !script->hasAnalysis() || js_IonOptions.eagerCompilation)
         return minUses;
 
     analyze::LoopAnalysis *loop = script->analysis()->getLoop(pc);
@@ -2048,46 +2051,46 @@ AutoFlushInhibitor::~AutoFlushInhibitor(
     ic_->setFlusher(afc);
     if (afc)
         IonSpewCont(IonSpew_CacheFlush, "{");
 }
 
 int js::ion::LabelBase::id_count = 0;
 
 void
-ion::PurgeCaches(JSScript *script, JSCompartment *c) {
+ion::PurgeCaches(UnrootedScript script, JSCompartment *c) {
     if (script->hasIonScript())
         script->ion->purgeCaches(c);
 
     if (script->hasParallelIonScript())
         script->ion->purgeCaches(c);
 }
 
 size_t
-ion::MemoryUsed(JSScript *script, JSMallocSizeOfFun mallocSizeOf) {
+ion::MemoryUsed(UnrootedScript script, JSMallocSizeOfFun mallocSizeOf) {
     size_t result = 0;
 
     if (script->hasIonScript())
         result += script->ion->sizeOfIncludingThis(mallocSizeOf);
 
     if (script->hasParallelIonScript())
         result += script->parallelIon->sizeOfIncludingThis(mallocSizeOf);
 
     return result;
 }
 
 void
-ion::DestroyIonScripts(FreeOp *fop, JSScript *script) {
+ion::DestroyIonScripts(FreeOp *fop, UnrootedScript script) {
     if (script->hasIonScript())
         ion::IonScript::Destroy(fop, script->ion);
 
     if (script->hasParallelIonScript())
         ion::IonScript::Destroy(fop, script->parallelIon);
 }
 
 void
-ion::TraceIonScripts(JSTracer* trc, JSScript *script) {
+ion::TraceIonScripts(JSTracer* trc, UnrootedScript script) {
     if (script->hasIonScript())
         ion::IonScript::Trace(trc, script->ion);
 
     if (script->hasParallelIonScript())
         ion::IonScript::Trace(trc, script->parallelIon);
 }
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -276,42 +276,42 @@ IonExecStatus SideCannon(JSContext *cx, 
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
 IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgsList &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
-bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
+bool Invalidate(JSContext *cx, UnrootedScript script, bool resetUses = true);
 
 void MarkValueFromIon(JSRuntime *rt, Value *vp);
 void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
 
 void ToggleBarriers(JSCompartment *comp, bool needs);
 
 class IonBuilder;
 class MIRGenerator;
 class CodeGenerator;
 
 CodeGenerator *CompileBackEnd(MIRGenerator *mir);
 void AttachFinishedCompilations(JSContext *cx);
 void FinishOffThreadBuilder(IonBuilder *builder);
-bool TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing);
+bool TestIonCompile(JSContext *cx, HandleScript script, HandleFunction fun, jsbytecode *osrPc, bool constructing);
 
 static inline bool IsEnabled(JSContext *cx)
 {
     return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
 }
 
-void ForbidCompilation(JSContext *cx, JSScript *script);
-uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
+void ForbidCompilation(JSContext *cx, UnrootedScript script);
+uint32_t UsesBeforeIonRecompile(UnrootedScript script, jsbytecode *pc);
 
-void PurgeCaches(JSScript *script, JSCompartment *c);
-size_t MemoryUsed(JSScript *script, JSMallocSizeOfFun mallocSizeOf);
-void DestroyIonScripts(FreeOp *fop, JSScript *script);
-void TraceIonScripts(JSTracer* trc, JSScript *script);
+void PurgeCaches(UnrootedScript script, JSCompartment *c);
+size_t MemoryUsed(UnrootedScript script, JSMallocSizeOfFun mallocSizeOf);
+void DestroyIonScripts(FreeOp *fop, UnrootedScript script);
+void TraceIonScripts(JSTracer* trc, UnrootedScript script);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_h__
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -790,17 +790,17 @@ TryAttachNativeGetPropStub(JSContext *cx
 }
 
 bool
 js::ion::GetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, MutableHandleValue vp)
 {
     AutoFlushCache afc ("GetPropertyCache");
     const SafepointIndex *safepointIndex;
     void *returnAddr;
-    JSScript *topScript = GetTopIonJSScript(cx, &safepointIndex, &returnAddr);
+    RootedScript topScript(cx, GetTopIonJSScript(cx, &safepointIndex, &returnAddr));
     IonScript *ion = topScript->ionScript();
 
     IonCacheGetProperty &cache = ion->getCache(cacheIndex).toGetProperty();
     RootedPropertyName name(cx, cache.name());
 
     RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
@@ -1343,17 +1343,17 @@ IsPropertyAddInlineable(JSContext *cx, H
 bool
 js::ion::SetPropertyCache(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue value,
                           bool isSetName)
 {
     AutoFlushCache afc ("SetPropertyCache");
 
     void *returnAddr;
     const SafepointIndex *safepointIndex;
-    JSScript *script = GetTopIonJSScript(cx, &safepointIndex, &returnAddr);
+    RootedScript script(cx, GetTopIonJSScript(cx, &safepointIndex, &returnAddr));
     IonScript *ion = script->ion;
     IonCacheSetProperty &cache = ion->getCache(cacheIndex).toSetProperty();
     RootedPropertyName name(cx, cache.name());
     RootedId id(cx, AtomToId(name));
     RootedShape shape(cx);
     RootedObject holder(cx);
 
     bool inlinable = IsPropertyInlineable(obj, cache);
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -223,17 +223,17 @@ class IonCache
         JS_ASSERT(kind_ == BindName);
         return *(IonCacheBindName *)this;
     }
     IonCacheName &toName() {
         JS_ASSERT(kind_ == Name || kind_ == NameTypeOf);
         return *(IonCacheName *)this;
     }
 
-    void setScriptedLocation(JSScript *script, jsbytecode *pc) {
+    void setScriptedLocation(UnrootedScript script, jsbytecode *pc) {
         JS_ASSERT(!idempotent_);
         this->script = script;
         this->pc = pc;
     }
 
     void getScriptedLocation(MutableHandleScript pscript, jsbytecode **ppc) {
         pscript.set(script);
         *ppc = pc;
--- a/js/src/ion/IonCode.h
+++ b/js/src/ion/IonCode.h
@@ -323,17 +323,17 @@ struct IonScript
         return snapshotsSize_;
     }
     const uint8_t *safepoints() const {
         return reinterpret_cast<const uint8_t *>(this) + safepointsStart_;
     }
     size_t safepointsSize() const {
         return safepointsSize_;
     }
-    JSScript *getScript(size_t i) const {
+    UnrootedScript getScript(size_t i) const {
         JS_ASSERT(i < scriptEntries_);
         return scriptList()[i];
     }
     size_t scriptEntries() const {
         return scriptEntries_;
     }
     size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
         return mallocSizeOf(this);
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -220,15 +220,15 @@ class IonActivation
     }
     static inline size_t offsetOfEntryFp() {
         return offsetof(IonActivation, entryfp_);
     }
 };
 
 // Called from JSCompartment::discardJitCode().
 void InvalidateAll(FreeOp *fop, JSCompartment *comp);
-void FinishInvalidation(FreeOp *fop, JSScript *script);
+void FinishInvalidation(FreeOp *fop, UnrootedScript script);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_compartment_h__
 
--- a/js/src/ion/IonFrameIterator.h
+++ b/js/src/ion/IonFrameIterator.h
@@ -124,17 +124,17 @@ class IonFrameIterator
     bool isConstructing() const;
 
     bool isEntryJSFrame() const;
 
     void *calleeToken() const;
     JSFunction *callee() const;
     JSFunction *maybeCallee() const;
     unsigned numActualArgs() const;
-    JSScript *script() const;
+    UnrootedScript script() const;
     Value *nativeVp() const;
     Value *actualArgs() const;
 
     // Returns the return address of the frame above this one (that is, the
     // return address that returns back to the current frame).
     uint8_t *returnAddressToFp() const {
         return returnAddressToFp_;
     }
@@ -286,18 +286,18 @@ class InlineFrameIterator
     JSFunction *maybeCallee() const {
         return callee_;
     }
     unsigned numActualArgs() const;
 
     template <class Op>
     inline void forEachCanonicalActualArg(Op op, unsigned start, unsigned count) const;
 
-    JSScript *script() const {
-        return script_;
+    UnrootedScript script() const {
+        return script_.get();
     }
     jsbytecode *pc() const {
         return pc_;
     }
     SnapshotIterator snapshotIterator() const {
         return si_;
     }
     bool isFunctionFrame() const;
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -150,17 +150,17 @@ IonFrameIterator::isEntryJSFrame() const
     ++iter;
     for (; !iter.done(); ++iter) {
         if (iter.isScripted())
             return false;
     }
     return true;
 }
 
-JSScript *
+UnrootedScript
 IonFrameIterator::script() const
 {
     AutoAssertNoGC nogc;
     JS_ASSERT(isScripted());
     RawScript script = ScriptFromCalleeToken(calleeToken());
     JS_ASSERT(script);
     return script;
 }
@@ -396,17 +396,17 @@ MarkCalleeToken(JSTracer *trc, CalleeTok
       {
         JSFunction *fun = CalleeTokenToFunction(token);
         MarkObjectRoot(trc, &fun, "ion-callee");
         JS_ASSERT(fun == CalleeTokenToFunction(token));
         break;
       }
       case CalleeToken_Script:
       {
-        JSScript *script = CalleeTokenToScript(token);
+        UnrootedScript script = CalleeTokenToScript(token);
         MarkScriptRoot(trc, &script, "ion-entry");
         JS_ASSERT(script == CalleeTokenToScript(token));
         break;
       }
       default:
         JS_NOT_REACHED("unknown callee token type");
     }
 }
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -642,17 +642,17 @@ class MacroAssembler : public MacroAssem
 
     void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, -1, temp, &stackFull);
         store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
         bind(&stackFull);
     }
 
-    void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
+    void spsPushFrame(SPSProfiler *p, const char *str, UnrootedScript s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
         storePtr(ImmWord(str),    Address(temp, ProfileEntry::offsetOfString()));
         storePtr(ImmGCPtr(s),     Address(temp, ProfileEntry::offsetOfScript()));
         storePtr(ImmWord((void*) NULL),
                  Address(temp, ProfileEntry::offsetOfStackAddress()));
         store32(Imm32(ProfileEntry::NullPCIndex),
--- a/js/src/ion/IonSpewer.cpp
+++ b/js/src/ion/IonSpewer.cpp
@@ -36,17 +36,17 @@ static const char *ChannelNames[] =
 
 void
 ion::EnableIonDebugLogging()
 {
     ionspewer.init();
 }
 
 void
-ion::IonSpewNewFunction(MIRGraph *graph, JSScript *function)
+ion::IonSpewNewFunction(MIRGraph *graph, HandleScript function)
 {
     if (!js_IonOptions.parallelCompilation)
         ionspewer.beginFunction(graph, function);
 }
 
 void
 ion::IonSpewPass(const char *pass)
 {
@@ -89,17 +89,17 @@ IonSpewer::init()
     if (!jsonSpewer.init(ION_SPEW_DIR "ion.json"))
         return false;
 
     inited_ = true;
     return true;
 }
 
 void
-IonSpewer::beginFunction(MIRGraph *graph, JSScript *function)
+IonSpewer::beginFunction(MIRGraph *graph, HandleScript function)
 {
     if (!inited_)
         return;
 
     this->graph = graph;
     this->function = function;
 
     c1Spewer.beginFunction(graph, function);
--- a/js/src/ion/IonSpewer.h
+++ b/js/src/ion/IonSpewer.h
@@ -67,37 +67,38 @@ enum IonSpewChannel {
 static const int NULL_ID = -1;
 
 #ifdef DEBUG
 
 class IonSpewer
 {
   private:
     MIRGraph *graph;
-    JSScript *function;
+    HandleScript function;
     C1Spewer c1Spewer;
     JSONSpewer jsonSpewer;
     bool inited_;
 
+
   public:
     IonSpewer()
-      : graph(NULL), function(NULL), inited_(false)
+      : graph(NULL), function(NullPtr()), inited_(false)
     { }
 
     // File output is terminated safely upon destruction.
     ~IonSpewer();
 
     bool init();
-    void beginFunction(MIRGraph *graph, JSScript *);
+    void beginFunction(MIRGraph *graph, HandleScript);
     void spewPass(const char *pass);
     void spewPass(const char *pass, LinearScanAllocator *ra);
     void endFunction();
 };
 
-void IonSpewNewFunction(MIRGraph *graph, JSScript *function);
+void IonSpewNewFunction(MIRGraph *graph, HandleScript function);
 void IonSpewPass(const char *pass);
 void IonSpewPass(const char *pass, LinearScanAllocator *ra);
 void IonSpewEndFunction();
 
 void CheckLogging();
 extern FILE *IonSpewFile;
 void IonSpew(IonSpewChannel channel, const char *fmt, ...);
 void IonSpewStart(IonSpewChannel channel, const char *fmt, ...);
@@ -110,17 +111,17 @@ void IonSpewStartVA(IonSpewChannel chann
 void IonSpewContVA(IonSpewChannel channel, const char *fmt, va_list ap);
 
 void EnableChannel(IonSpewChannel channel);
 void DisableChannel(IonSpewChannel channel);
 void EnableIonDebugLogging();
 
 #else
 
-static inline void IonSpewNewFunction(MIRGraph *graph, JSScript *function)
+static inline void IonSpewNewFunction(MIRGraph *graph, HandleScript function)
 { }
 static inline void IonSpewPass(const char *pass)
 { }
 static inline void IonSpewPass(const char *pass, LinearScanAllocator *ra)
 { }
 static inline void IonSpewEndFunction()
 { }
 
--- a/js/src/ion/JSONSpewer.cpp
+++ b/js/src/ion/JSONSpewer.cpp
@@ -173,17 +173,17 @@ JSONSpewer::init(const char *path)
         return false;
 
     beginObject();
     beginListProperty("functions");
     return true;
 }
 
 void
-JSONSpewer::beginFunction(JSScript *script)
+JSONSpewer::beginFunction(UnrootedScript script)
 {
     if (inFunction_)
         endFunction();
 
     beginObject();
     stringProperty("name", "%s:%d", script->filename, script->lineno);
     beginListProperty("passes");
 
--- a/js/src/ion/JSONSpewer.h
+++ b/js/src/ion/JSONSpewer.h
@@ -5,16 +5,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ion_jsonspewer_h__
 #define js_ion_jsonspewer_h__
 
 #include <stdio.h>
 
+#include "gc/Root.h"
+#include "jsscript.h"
+
 struct JSScript;
 
 namespace js {
 namespace ion {
 
 class MDefinition;
 class MInstruction;
 class MBasicBlock;
@@ -52,17 +55,17 @@ class JSONSpewer
       : inFunction_(false),
         indentLevel_(0),
         first_(true),
         fp_(NULL)
     { }
     ~JSONSpewer();
 
     bool init(const char *path);
-    void beginFunction(JSScript *script);
+    void beginFunction(UnrootedScript script);
     void beginPass(const char * pass);
     void spewMDef(MDefinition *def);
     void spewMResumePoint(MResumePoint *rp);
     void spewMIR(MIRGraph *mir);
     void spewLIns(LInstruction *ins);
     void spewLIR(MIRGraph *mir);
     void spewIntervals(LinearScanAllocator *regalloc);
     void endPass();
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -3259,17 +3259,17 @@ class LFunctionBoundary : public LInstru
     LFunctionBoundary(const LDefinition &temp) {
         setTemp(0, temp);
     }
 
     const LDefinition *temp() {
         return getTemp(0);
     }
 
-    JSScript *script() {
+    UnrootedScript script() {
         return mir_->toFunctionBoundary()->script();
     }
 
     MFunctionBoundary::Type type() {
         return mir_->toFunctionBoundary()->type();
     }
 
     unsigned inlineLevel() {
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -4468,40 +4468,40 @@ class MGetElementCache
 class MBindNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     CompilerRootPropertyName name_;
     CompilerRootScript script_;
     jsbytecode *pc_;
 
-    MBindNameCache(MDefinition *scopeChain, PropertyName *name, JSScript *script, jsbytecode *pc)
+    MBindNameCache(MDefinition *scopeChain, PropertyName *name, UnrootedScript script, jsbytecode *pc)
       : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(BindNameCache)
 
-    static MBindNameCache *New(MDefinition *scopeChain, PropertyName *name, JSScript *script,
+    static MBindNameCache *New(MDefinition *scopeChain, PropertyName *name, UnrootedScript script,
                                jsbytecode *pc) {
         return new MBindNameCache(scopeChain, name, script, pc);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
     MDefinition *scopeChain() const {
         return getOperand(0);
     }
     PropertyName *name() const {
         return name_;
     }
-    JSScript *script() const {
+    UnrootedScript script() const {
         return script_;
     }
     jsbytecode *pc() const {
         return pc_;
     }
 };
 
 // Guard on an object's shape.
@@ -5670,33 +5670,33 @@ class MFunctionBoundary : public MNullar
                       // before this boundary
     };
 
   private:
     JSScript *script_;
     Type type_;
     unsigned inlineLevel_;
 
-    MFunctionBoundary(JSScript *script, Type type, unsigned inlineLevel)
+    MFunctionBoundary(UnrootedScript script, Type type, unsigned inlineLevel)
       : script_(script), type_(type), inlineLevel_(inlineLevel)
     {
         JS_ASSERT_IF(type != Inline_Exit, script != NULL);
         JS_ASSERT_IF(type == Inline_Enter, inlineLevel != 0);
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(FunctionBoundary)
 
-    static MFunctionBoundary *New(JSScript *script, Type type,
+    static MFunctionBoundary *New(UnrootedScript script, Type type,
                                   unsigned inlineLevel = 0) {
         return new MFunctionBoundary(script, type, inlineLevel);
     }
 
-    JSScript *script() {
+    UnrootedScript script() {
         return script_;
     }
 
     Type type() {
         return type_;
     }
 
     unsigned inlineLevel() {
--- a/js/src/ion/MIRGraph.h
+++ b/js/src/ion/MIRGraph.h
@@ -463,17 +463,17 @@ class MIRGraph
     TempAllocator *alloc_;
     MIRGraphExits *exitAccumulator_;
     uint32_t blockIdGen_;
     uint32_t idGen_;
     MBasicBlock *osrBlock_;
     MStart *osrStart_;
 
     // List of compiled/inlined scripts.
-    Vector<JSScript *, 4, IonAllocPolicy> scripts_;
+    Vector<RawScript, 4, IonAllocPolicy> scripts_;
 
     size_t numBlocks_;
 
   public:
     MIRGraph(TempAllocator *alloc)
       : alloc_(alloc),
         exitAccumulator_(NULL),
         blockIdGen_(0),
@@ -582,17 +582,17 @@ class MIRGraph
         return osrBlock_;
     }
     void setOsrStart(MStart *osrStart) {
         osrStart_ = osrStart;
     }
     MStart *osrStart() {
         return osrStart_;
     }
-    bool addScript(JSScript *script) {
+    bool addScript(UnrootedScript script) {
         // The same script may be inlined multiple times, add it only once.
         for (size_t i = 0; i < scripts_.length(); i++) {
             if (scripts_[i] == script)
                 return true;
         }
         return scripts_.append(script);
     }
     size_t numScripts() const {
--- a/js/src/ion/PcScriptCache.h
+++ b/js/src/ion/PcScriptCache.h
@@ -15,17 +15,17 @@ struct JSRuntime;
 
 namespace js {
 namespace ion {
 
 struct PcScriptCacheEntry
 {
     uint8_t *returnAddress; // Key into the hash table.
     jsbytecode *pc;         // Cached PC.
-    JSScript *script;       // Cached script.
+    RawScript script;       // Cached script.
 };
 
 struct PcScriptCache
 {
     static const uint32_t Length = 73;
 
     // GC number at the time the cache was filled or created.
     // Storing and checking against this number allows us to not bother
@@ -41,17 +41,17 @@ struct PcScriptCache
         this->gcNumber = gcNumber;
     }
 
     // Get a value from the cache. May perform lazy allocation.
     // Defined in PcScriptCache-inl.h.
     bool get(JSRuntime *rt, uint32_t hash, uint8_t *addr,
              MutableHandleScript scriptRes, jsbytecode **pcRes);
 
-    void add(uint32_t hash, uint8_t *addr, jsbytecode *pc, JSScript *script) {
+    void add(uint32_t hash, uint8_t *addr, jsbytecode *pc, UnrootedScript script) {
         entries[hash].returnAddress = addr;
         entries[hash].pc = pc;
         entries[hash].script = script;
     }
 
     static uint32_t Hash(uint8_t *addr) {
         uint32_t key = (uint32_t)((uintptr_t)addr);
         return ((key >> 3) * 2654435761u) % Length;
--- a/js/src/ion/SnapshotWriter.h
+++ b/js/src/ion/SnapshotWriter.h
@@ -29,17 +29,17 @@ class SnapshotWriter
     uint32_t nframes_;
     uint32_t framesWritten_;
     SnapshotOffset lastStart_;
 
     void writeSlotHeader(JSValueType type, uint32_t regCode);
 
   public:
     SnapshotOffset startSnapshot(uint32_t frameCount, BailoutKind kind, bool resumeAfter);
-    void startFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack);
+    void startFrame(JSFunction *fun, UnrootedScript script, jsbytecode *pc, uint32_t exprStack);
 #ifdef TRACK_SNAPSHOTS
     void trackFrame(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId,
                                      uint32_t lirOpcode, uint32_t lirId);
 #endif
     void endFrame();
 
     void addSlot(const FloatRegister &reg);
     void addSlot(JSValueType type, const Register &reg);
--- a/js/src/ion/Snapshots.cpp
+++ b/js/src/ion/Snapshots.cpp
@@ -26,17 +26,17 @@ using namespace js::ion;
 // Snapshot header:
 //
 //   [vwu] bits (n-31]: frame count
 //         bits [0,n):  bailout kind (n = BAILOUT_KIND_BITS)
 //
 // Snapshot body, repeated "frame count" times, from oldest frame to newest frame.
 // Note that the first frame doesn't have the "parent PC" field.
 //
-//   [ptr] Debug only: JSScript *
+//   [ptr] Debug only: RawScript
 //   [vwu] pc offset
 //   [vwu] # of slots, including nargs
 // [slot*] N slot entries, where N = nargs + nfixed + stackDepth
 //
 // Encodings:
 //   [ptr] A fixed-size pointer.
 //   [vwu] A variable-width unsigned integer.
 //   [vws] A variable-width signed integer.
@@ -131,20 +131,20 @@ SnapshotReader::readSnapshotHeader()
 void
 SnapshotReader::readFrameHeader()
 {
     JS_ASSERT(moreFrames());
     JS_ASSERT(slotsRead_ == slotCount_);
 
 #ifdef DEBUG
     union {
-        JSScript *script;
-        uint8_t bytes[sizeof(JSScript *)];
+        RawScript script;
+        uint8_t bytes[sizeof(RawScript)];
     } u;
-    for (size_t i = 0; i < sizeof(JSScript *); i++)
+    for (size_t i = 0; i < sizeof(RawScript); i++)
         u.bytes[i] = reader_.readByte();
     script_ = u.script;
 #endif
 
     pcOffset_ = reader_.readUnsigned();
     slotCount_ = reader_.readUnsigned();
     IonSpew(IonSpew_Snapshots, "Read pc offset %u, nslots %u", pcOffset_, slotCount_);
 
@@ -302,36 +302,36 @@ SnapshotWriter::startSnapshot(uint32_t f
     if (resumeAfter)
         bits |= (1 << BAILOUT_RESUME_SHIFT);
 
     writer_.writeUnsigned(bits);
     return lastStart_;
 }
 
 void
-SnapshotWriter::startFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack)
+SnapshotWriter::startFrame(JSFunction *fun, UnrootedScript script, jsbytecode *pc, uint32_t exprStack)
 {
     JS_ASSERT(CountArgSlots(fun) < SNAPSHOT_MAX_NARGS);
     JS_ASSERT(exprStack < SNAPSHOT_MAX_STACK);
 
     uint32_t formalArgs = CountArgSlots(fun);
 
     nslots_ = formalArgs + script->nfixed + exprStack;
     slotsWritten_ = 0;
 
     IonSpew(IonSpew_Snapshots, "Starting frame; formals %u, fixed %u, exprs %u",
             formalArgs, script->nfixed, exprStack);
 
 #ifdef DEBUG
     union {
-        JSScript *script;
-        uint8_t bytes[sizeof(JSScript *)];
+        RawScript script;
+        uint8_t bytes[sizeof(RawScript)];
     } u;
     u.script = script;
-    for (size_t i = 0; i < sizeof(JSScript *); i++)
+    for (size_t i = 0; i < sizeof(RawScript); i++)
         writer_.writeByte(u.bytes[i]);
 #endif
 
     JS_ASSERT(script->code <= pc && pc <= script->code + script->length);
 
     uint32_t pcoff = uint32_t(pc - script->code);
     IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nslots_);
     writer_.writeUnsigned(pcoff);
--- a/js/src/ion/TypeOracle.cpp
+++ b/js/src/ion/TypeOracle.cpp
@@ -13,21 +13,21 @@
 #include "jsanalyze.h"
 
 using namespace js;
 using namespace js::ion;
 using namespace js::types;
 using namespace js::analyze;
 
 bool
-TypeInferenceOracle::init(JSContext *cx, JSScript *script)
+TypeInferenceOracle::init(JSContext *cx, HandleScript script)
 {
     this->cx = cx;
     this->script_.init(script);
-    return script->ensureRanInference(cx);
+    return JSScript::ensureRanInference(cx, script);
 }
 
 MIRType
 GetMIRType(JSValueType type)
 {
     /* Get the suggested representation to use for values in a given type set. */
     switch (type) {
       case JSVAL_TYPE_UNDEFINED:
@@ -59,28 +59,28 @@ TypeInferenceOracle::getMIRType(StackTyp
 
 MIRType
 TypeInferenceOracle::getMIRType(HeapTypeSet *types)
 {
     return GetMIRType(types->getKnownTypeTag(cx));
 }
 
 TypeOracle::UnaryTypes
-TypeInferenceOracle::unaryTypes(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::unaryTypes(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(script == this->script());
 
     UnaryTypes res;
     res.inTypes = script->analysis()->poppedTypes(pc, 0);
     res.outTypes = script->analysis()->pushedTypes(pc, 0);
     return res;
 }
 
 TypeOracle::BinaryTypes
-TypeInferenceOracle::binaryTypes(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::binaryTypes(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(script == this->script());
 
     JSOp op = (JSOp)*pc;
 
     BinaryTypes res;
     if (op == JSOP_NEG || op == JSOP_POS) {
         res.lhsTypes = script->analysis()->poppedTypes(pc, 0);
@@ -90,28 +90,28 @@ TypeInferenceOracle::binaryTypes(JSScrip
         res.lhsTypes = script->analysis()->poppedTypes(pc, 1);
         res.rhsTypes = script->analysis()->poppedTypes(pc, 0);
         res.outTypes = script->analysis()->pushedTypes(pc, 0);
     }
     return res;
 }
 
 TypeOracle::Unary
-TypeInferenceOracle::unaryOp(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::unaryOp(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(script == this->script());
 
     Unary res;
     res.ival = getMIRType(script->analysis()->poppedTypes(pc, 0));
     res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
     return res;
 }
 
 TypeOracle::Binary
-TypeInferenceOracle::binaryOp(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::binaryOp(UnrootedScript script, jsbytecode *pc)
 {
     JS_ASSERT(script == this->script());
 
     JSOp op = (JSOp)*pc;
 
     Binary res;
     if (op == JSOP_NEG || op == JSOP_POS) {
         res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 0));
@@ -121,17 +121,17 @@ TypeInferenceOracle::binaryOp(JSScript *
         res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 1));
         res.rhs = getMIRType(script->analysis()->poppedTypes(pc, 0));
         res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
     }
     return res;
 }
 
 StackTypeSet *
-TypeInferenceOracle::thisTypeSet(JSScript *script)
+TypeInferenceOracle::thisTypeSet(UnrootedScript script)
 {
     JS_ASSERT(script == this->script());
     return TypeScript::ThisTypes(script);
 }
 
 bool
 TypeInferenceOracle::getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes)
 {
@@ -200,43 +200,43 @@ TypeInferenceOracle::getOsrTypes(jsbytec
         for (size_t i = ArgSlot(0); i < TotalSlots(script()); i++)
             slotTypes[i - 1] = getMIRType(slotTypeSets[i]);
     }
 
     return true;
 }
 
 StackTypeSet *
-TypeInferenceOracle::parameterTypeSet(JSScript *script, size_t index)
+TypeInferenceOracle::parameterTypeSet(UnrootedScript script, size_t index)
 {
     JS_ASSERT(script == this->script());
     return TypeScript::ArgTypes(script, index);
 }
 
 StackTypeSet *
-TypeInferenceOracle::propertyRead(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::propertyRead(UnrootedScript script, jsbytecode *pc)
 {
     return script->analysis()->pushedTypes(pc, 0);
 }
 
 StackTypeSet *
-TypeInferenceOracle::propertyReadBarrier(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::propertyReadBarrier(HandleScript script, jsbytecode *pc)
 {
     if (script->analysis()->typeBarriers(cx, pc))
         return script->analysis()->bytecodeTypes(pc);
     return NULL;
 }
 
 bool
-TypeInferenceOracle::propertyReadIdempotent(JSScript *script, jsbytecode *pc, HandleId id)
+TypeInferenceOracle::propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id)
 {
     if (script->analysis()->getCode(pc).notIdempotent)
         return false;
 
-    if (id.get() != MakeTypeId(cx, id))
+    if (id != MakeTypeId(cx, id))
         return false;
 
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 0);
     if (!types || types->unknownObject())
         return false;
 
     for (unsigned i = 0; i < types->getObjectCount(); i++) {
         if (types->getSingleObject(i))
@@ -252,17 +252,17 @@ TypeInferenceOracle::propertyReadIdempot
                 return false;
         }
     }
 
     return true;
 }
 
 bool
-TypeInferenceOracle::propertyReadAccessGetter(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::propertyReadAccessGetter(UnrootedScript script, jsbytecode *pc)
 {
     return script->analysis()->getCode(pc).accessGetter;
 }
 
 bool
 TypeInferenceOracle::inObjectIsDenseArray(HandleScript script, jsbytecode *pc)
 {
     // Check whether the object is a dense array and index is int32 or double.
@@ -276,24 +276,24 @@ TypeInferenceOracle::inObjectIsDenseArra
     JSValueType objType = obj->getKnownTypeTag();
     if (objType != JSVAL_TYPE_OBJECT)
         return false;
 
     return !obj->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY);
 }
 
 bool
-TypeInferenceOracle::inArrayIsPacked(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::inArrayIsPacked(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 0);
     return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
 }
 
 bool
-TypeInferenceOracle::elementReadIsDenseArray(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementReadIsDenseArray(UnrootedScript script, jsbytecode *pc)
 {
     // Check whether the object is a dense array and index is int32 or double.
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
     StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
 
     JSValueType objType = obj->getKnownTypeTag();
     if (objType != JSVAL_TYPE_OBJECT)
         return false;
@@ -301,17 +301,17 @@ TypeInferenceOracle::elementReadIsDenseA
     JSValueType idType = id->getKnownTypeTag();
     if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
         return false;
 
     return !obj->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY);
 }
 
 bool
-TypeInferenceOracle::elementReadIsTypedArray(JSScript *script, jsbytecode *pc, int *arrayType)
+TypeInferenceOracle::elementReadIsTypedArray(UnrootedScript script, jsbytecode *pc, int *arrayType)
 {
     // Check whether the object is a typed array and index is int32 or double.
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
     StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
 
     JSValueType objType = obj->getKnownTypeTag();
     if (objType != JSVAL_TYPE_OBJECT)
         return false;
@@ -341,17 +341,17 @@ TypeInferenceOracle::elementReadIsTypedA
         if (!result->hasType(types::Type::Int32Type()))
             return false;
     }
 
     return true;
 }
 
 bool
-TypeInferenceOracle::elementReadIsString(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementReadIsString(UnrootedScript script, jsbytecode *pc)
 {
     // Check for string[index].
     StackTypeSet *value = script->analysis()->poppedTypes(pc, 1);
     StackTypeSet *id = script->analysis()->poppedTypes(pc, 0);
 
     if (value->getKnownTypeTag() != JSVAL_TYPE_STRING)
         return false;
 
@@ -365,24 +365,24 @@ TypeInferenceOracle::elementReadIsString
     StackTypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
     if (pushed->getKnownTypeTag() != JSVAL_TYPE_STRING)
         return false;
 
     return true;
 }
 
 bool
-TypeInferenceOracle::elementReadIsPacked(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementReadIsPacked(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 1);
     return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
 }
 
 void
-TypeInferenceOracle::elementReadGeneric(JSScript *script, jsbytecode *pc, bool *cacheable, bool *monitorResult)
+TypeInferenceOracle::elementReadGeneric(UnrootedScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult)
 {
     MIRType obj = getMIRType(script->analysis()->poppedTypes(pc, 1));
     MIRType id = getMIRType(script->analysis()->poppedTypes(pc, 0));
 
     *cacheable = (obj == MIRType_Object &&
                   (id == MIRType_Value || id == MIRType_Int32 || id == MIRType_String));
 
     // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
@@ -410,17 +410,17 @@ TypeInferenceOracle::elementWriteIsDense
     JSValueType idType = id->getKnownTypeTag();
     if (idType != JSVAL_TYPE_INT32 && idType != JSVAL_TYPE_DOUBLE)
         return false;
 
     return !obj->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY);
 }
 
 bool
-TypeInferenceOracle::elementWriteIsTypedArray(JSScript *script, jsbytecode *pc, int *arrayType)
+TypeInferenceOracle::elementWriteIsTypedArray(UnrootedScript script, jsbytecode *pc, int *arrayType)
 {
     // Check whether the object is a dense array and index is int32 or double.
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
     StackTypeSet *id = script->analysis()->poppedTypes(pc, 1);
 
     JSValueType objType = obj->getKnownTypeTag();
     if (objType != JSVAL_TYPE_OBJECT)
         return false;
@@ -435,30 +435,30 @@ TypeInferenceOracle::elementWriteIsTyped
     *arrayType = obj->getTypedArrayType();
     if (*arrayType == TypedArray::TYPE_MAX)
         return false;
 
     return true;
 }
 
 bool
-TypeInferenceOracle::elementWriteIsPacked(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementWriteIsPacked(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 2);
     return !types->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY);
 }
 
 bool
-TypeInferenceOracle::setElementHasWrittenHoles(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc)
 {
     return script->analysis()->getCode(pc).arrayWriteHole;
 }
 
 MIRType
-TypeInferenceOracle::elementWrite(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementWrite(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *objTypes = script->analysis()->poppedTypes(pc, 2);
     MIRType elementType = MIRType_None;
     unsigned count = objTypes->getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
         if (objTypes->getSingleObject(i))
             return MIRType_None;
@@ -497,58 +497,58 @@ TypeInferenceOracle::canInlineCalls()
 
 bool
 TypeInferenceOracle::propertyWriteCanSpecialize(UnrootedScript script, jsbytecode *pc)
 {
     return !script->analysis()->getCode(pc).monitoredTypes;
 }
 
 bool
-TypeInferenceOracle::propertyWriteNeedsBarrier(JSScript *script, jsbytecode *pc, jsid id)
+TypeInferenceOracle::propertyWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc, jsid id)
 {
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 1);
     return types->propertyNeedsBarrier(cx, id);
 }
 
 bool
-TypeInferenceOracle::elementWriteNeedsBarrier(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc)
 {
     // Return true if SETELEM-like instructions need a write barrier before modifying
     // a property. The object is the third value popped by SETELEM.
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 2);
     return types->propertyNeedsBarrier(cx, JSID_VOID);
 }
 
 StackTypeSet *
-TypeInferenceOracle::getCallTarget(JSScript *caller, uint32_t argc, jsbytecode *pc)
+TypeInferenceOracle::getCallTarget(UnrootedScript caller, uint32_t argc, jsbytecode *pc)
 {
     JS_ASSERT(caller == this->script());
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE && JSOp(*pc) != JSOP_EVAL);
 
     ScriptAnalysis *analysis = script()->analysis();
     return analysis->poppedTypes(pc, argc + 1);
 }
 
 StackTypeSet *
-TypeInferenceOracle::getCallArg(JSScript *script, uint32_t argc, uint32_t arg, jsbytecode *pc)
+TypeInferenceOracle::getCallArg(UnrootedScript script, uint32_t argc, uint32_t arg, jsbytecode *pc)
 {
     JS_ASSERT(argc >= arg);
     // Bytecode order: Function, This, Arg0, Arg1, ..., ArgN, Call.
     // |argc| does not include |this|.
     return script->analysis()->poppedTypes(pc, argc - arg);
 }
 
 StackTypeSet *
-TypeInferenceOracle::getCallReturn(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::getCallReturn(UnrootedScript script, jsbytecode *pc)
 {
     return script->analysis()->pushedTypes(pc, 0);
 }
 
 bool
-TypeInferenceOracle::canInlineCall(JSScript *caller, jsbytecode *pc)
+TypeInferenceOracle::canInlineCall(HandleScript caller, jsbytecode *pc)
 {
     JS_ASSERT(types::IsInlinableCall(pc));
 
     Bytecode *code = caller->analysis()->maybeCode(pc);
     if (code->monitoredTypes || code->monitoredTypesReturn || caller->analysis()->typeBarriers(cx, pc))
         return false;
     return true;
 }
@@ -571,45 +571,45 @@ TypeInferenceOracle::canEnterInlinedFunc
         return false;
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     HeapTypeSet::WatchObjectStateChange(cx, target->getType(cx));
     return true;
 }
 
 HeapTypeSet *
-TypeInferenceOracle::globalPropertyWrite(JSScript *script, jsbytecode *pc, jsid id,
+TypeInferenceOracle::globalPropertyWrite(UnrootedScript script, jsbytecode *pc, jsid id,
                                          bool *canSpecialize)
 {
     *canSpecialize = !script->analysis()->getCode(pc).monitoredTypes;
     if (!*canSpecialize)
         return NULL;
 
     return globalPropertyTypeSet(script, pc, id);
 }
 
 StackTypeSet *
-TypeInferenceOracle::returnTypeSet(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier)
+TypeInferenceOracle::returnTypeSet(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier)
 {
     if (script->analysis()->getCode(pc).monitoredTypesReturn)
         *barrier = script->analysis()->bytecodeTypes(pc);
     else
         *barrier = NULL;
     return script->analysis()->pushedTypes(pc, 0);
 }
 
 StackTypeSet *
-TypeInferenceOracle::aliasedVarBarrier(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier)
+TypeInferenceOracle::aliasedVarBarrier(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier)
 {
     *barrier = script->analysis()->bytecodeTypes(pc);
     return script->analysis()->pushedTypes(pc, 0);
 }
 
 HeapTypeSet *
-TypeInferenceOracle::globalPropertyTypeSet(JSScript *script, jsbytecode *pc, jsid id)
+TypeInferenceOracle::globalPropertyTypeSet(UnrootedScript script, jsbytecode *pc, jsid id)
 {
     TypeObject *type = script->global().getType(cx);
     if (type->unknownProperties())
         return NULL;
 
     return type->getProperty(cx, id, false);
 }
 
@@ -619,27 +619,27 @@ TypeInferenceOracle::isArgumentObject(ty
     if (obj->isMagicArguments())
         return DefinitelyArguments;
     if (obj->hasAnyFlag(TYPE_FLAG_LAZYARGS))
         return MaybeArguments;
     return NotArguments;
 }
 
 LazyArgumentsType
-TypeInferenceOracle::propertyReadMagicArguments(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::propertyReadMagicArguments(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 0);
     return isArgumentObject(obj);
 }
 
 LazyArgumentsType
-TypeInferenceOracle::elementReadMagicArguments(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementReadMagicArguments(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 1);
     return isArgumentObject(obj);
 }
 
 LazyArgumentsType
-TypeInferenceOracle::elementWriteMagicArguments(JSScript *script, jsbytecode *pc)
+TypeInferenceOracle::elementWriteMagicArguments(UnrootedScript script, jsbytecode *pc)
 {
     StackTypeSet *obj = script->analysis()->poppedTypes(pc, 2);
     return isArgumentObject(obj);
 }
--- a/js/src/ion/TypeOracle.h
+++ b/js/src/ion/TypeOracle.h
@@ -39,159 +39,161 @@ class TypeOracle
     };
     struct Binary {
         MIRType lhs;
         MIRType rhs;
         MIRType rval;
     };
 
   public:
-    virtual UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc) = 0;
-    virtual BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc) = 0;
-    virtual Unary unaryOp(JSScript *script, jsbytecode *pc) = 0;
-    virtual Binary binaryOp(JSScript *script, jsbytecode *pc) = 0;
-    virtual types::StackTypeSet *thisTypeSet(JSScript *script) { return NULL; }
+    virtual UnaryTypes unaryTypes(UnrootedScript script, jsbytecode *pc) = 0;
+    virtual BinaryTypes binaryTypes(UnrootedScript script, jsbytecode *pc) = 0;
+    virtual Unary unaryOp(UnrootedScript script, jsbytecode *pc) = 0;
+    virtual Binary binaryOp(UnrootedScript script, jsbytecode *pc) = 0;
+    virtual types::StackTypeSet *thisTypeSet(UnrootedScript script) { return NULL; }
     virtual bool getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes) { return true; }
-    virtual types::StackTypeSet *parameterTypeSet(JSScript *script, size_t index) { return NULL; }
-    virtual types::HeapTypeSet *globalPropertyTypeSet(JSScript *script, jsbytecode *pc, jsid id) {
+    virtual types::StackTypeSet *parameterTypeSet(UnrootedScript script, size_t index) { return NULL; }
+    virtual types::HeapTypeSet *globalPropertyTypeSet(UnrootedScript script, jsbytecode *pc, jsid id) {
         return NULL;
     }
-    virtual types::StackTypeSet *propertyRead(JSScript *script, jsbytecode *pc) {
+    virtual types::StackTypeSet *propertyRead(UnrootedScript script, jsbytecode *pc) {
         return NULL;
     }
-    virtual types::StackTypeSet *propertyReadBarrier(JSScript *script, jsbytecode *pc) {
+    virtual types::StackTypeSet *propertyReadBarrier(HandleScript script, jsbytecode *pc) {
         return NULL;
     }
-    virtual bool propertyReadIdempotent(JSScript *script, jsbytecode *pc, HandleId id) {
+    virtual bool propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id) {
         return false;
     }
-    virtual bool propertyReadAccessGetter(JSScript *script, jsbytecode *pc) {
+    virtual bool propertyReadAccessGetter(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
-    virtual types::HeapTypeSet *globalPropertyWrite(JSScript *script, jsbytecode *pc,
+    virtual types::HeapTypeSet *globalPropertyWrite(UnrootedScript script, jsbytecode *pc,
                                                 jsid id, bool *canSpecialize) {
         *canSpecialize = true;
         return NULL;
     }
-    virtual types::StackTypeSet *returnTypeSet(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier) {
+    virtual types::StackTypeSet *returnTypeSet(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier) {
         *barrier = NULL;
         return NULL;
     }
     virtual bool inObjectIsDenseArray(HandleScript script, jsbytecode *pc) {
         return false;
     }
-    virtual bool inArrayIsPacked(JSScript *script, jsbytecode *pc) {
+    virtual bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
-    virtual bool elementReadIsDenseArray(JSScript *script, jsbytecode *pc) {
+    virtual bool elementReadIsDenseArray(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
-    virtual bool elementReadIsTypedArray(JSScript *script, jsbytecode *pc, int *arrayType) {
+    virtual bool elementReadIsTypedArray(UnrootedScript script, jsbytecode *pc, int *arrayType) {
         return false;
     }
-    virtual bool elementReadIsString(JSScript *script, jsbytecode *pc) {
+    virtual bool elementReadIsString(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
-    virtual bool elementReadIsPacked(JSScript *script, jsbytecode *pc) {
+    virtual bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
-    virtual void elementReadGeneric(JSScript *script, jsbytecode *pc, bool *cacheable, bool *monitorResult) {
+    virtual void elementReadGeneric(UnrootedScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult) {
         *cacheable = false;
         *monitorResult = true;
     }
-    virtual bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc) {
+    virtual bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc) {
         return true;
     }
     virtual bool elementWriteIsDenseArray(HandleScript script, jsbytecode *pc) {
         return false;
     }
-    virtual bool elementWriteIsTypedArray(JSScript *script, jsbytecode *pc, int *arrayType) {
+    virtual bool elementWriteIsTypedArray(UnrootedScript script, jsbytecode *pc, int *arrayType) {
         return false;
     }
-    virtual bool elementWriteIsPacked(JSScript *script, jsbytecode *pc) {
+    virtual bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc) {
         return false;
     }
     virtual bool propertyWriteCanSpecialize(UnrootedScript script, jsbytecode *pc) {
         return true;
     }
-    virtual bool propertyWriteNeedsBarrier(JSScript *script, jsbytecode *pc, jsid id) {
+    virtual bool propertyWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc, jsid id) {
         return true;
     }
-    virtual bool elementWriteNeedsBarrier(JSScript *script, jsbytecode *pc) {
+    virtual bool elementWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc) {
         return true;
     }
-    virtual MIRType elementWrite(JSScript *script, jsbytecode *pc) {
+    virtual MIRType elementWrite(UnrootedScript script, jsbytecode *pc) {
         return MIRType_None;
     }
     virtual bool arrayPrototypeHasIndexedProperty() {
         return true;
     }
     virtual bool canInlineCalls() {
         return false;
     }
 
     /* |pc| must be a |JSOP_CALL|. */
-    virtual types::StackTypeSet *getCallTarget(JSScript *caller, uint32_t argc, jsbytecode *pc) {
+    virtual types::StackTypeSet *getCallTarget(UnrootedScript caller, uint32_t argc, jsbytecode *pc) {
         // Same assertion as TypeInferenceOracle::getCallTarget.
         JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE && JSOp(*pc) != JSOP_EVAL);
         return NULL;
     }
-    virtual types::StackTypeSet *getCallArg(JSScript *script, uint32_t argc, uint32_t arg, jsbytecode *pc) {
+    virtual types::StackTypeSet *getCallArg(UnrootedScript script, uint32_t argc, uint32_t arg, jsbytecode *pc) {
         return NULL;
     }
-    virtual types::StackTypeSet *getCallReturn(JSScript *script, jsbytecode *pc) {
+    virtual types::StackTypeSet *getCallReturn(UnrootedScript script, jsbytecode *pc) {
         return NULL;
     }
-    virtual bool canInlineCall(JSScript *caller, jsbytecode *pc) {
+    virtual bool canInlineCall(HandleScript caller, jsbytecode *pc) {
         return false;
     }
     virtual bool canEnterInlinedFunction(JSFunction *callee) {
         return false;
     }
 
     virtual LazyArgumentsType isArgumentObject(types::StackTypeSet *obj) {
         return MaybeArguments;
     }
-    virtual LazyArgumentsType propertyReadMagicArguments(JSScript *script, jsbytecode *pc) {
+    virtual LazyArgumentsType propertyReadMagicArguments(UnrootedScript script, jsbytecode *pc) {
+        return MaybeArguments;
+    }
+    virtual LazyArgumentsType elementReadMagicArguments(UnrootedScript script, jsbytecode *pc) {
         return MaybeArguments;
     }
-    virtual LazyArgumentsType elementReadMagicArguments(JSScript *script, jsbytecode *pc) {
+    virtual LazyArgumentsType elementWriteMagicArguments(UnrootedScript script, jsbytecode *pc) {
         return MaybeArguments;
     }
-    virtual LazyArgumentsType elementWriteMagicArguments(JSScript *script, jsbytecode *pc) {
-        return MaybeArguments;
-    }
-    virtual types::StackTypeSet *aliasedVarBarrier(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier) {
+    virtual types::StackTypeSet *aliasedVarBarrier(UnrootedScript script, jsbytecode *pc,
+                                                   types::StackTypeSet **barrier)
+    {
         return NULL;
     }
 };
 
 class DummyOracle : public TypeOracle
 {
   public:
-    UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc) {
+    UnaryTypes unaryTypes(UnrootedScript script, jsbytecode *pc) {
         UnaryTypes u;
         u.inTypes = NULL;
         u.outTypes = NULL;
         return u;
     }
-    BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc) {
+    BinaryTypes binaryTypes(UnrootedScript script, jsbytecode *pc) {
         BinaryTypes b;
         b.lhsTypes = NULL;
         b.rhsTypes = NULL;
         b.outTypes = NULL;
         return b;
     }
-    Unary unaryOp(JSScript *script, jsbytecode *pc) {
+    Unary unaryOp(UnrootedScript script, jsbytecode *pc) {
         Unary u;
         u.ival = MIRType_Int32;
         u.rval = MIRType_Int32;
         return u;
     }
-    Binary binaryOp(JSScript *script, jsbytecode *pc) {
+    Binary binaryOp(UnrootedScript script, jsbytecode *pc) {
         Binary b;
         b.lhs = MIRType_Int32;
         b.rhs = MIRType_Int32;
         b.rval = MIRType_Int32;
         return b;
     }
 };
 
@@ -201,62 +203,62 @@ class TypeInferenceOracle : public TypeO
     HeapPtrScript script_;
 
     MIRType getMIRType(types::StackTypeSet *types);
     MIRType getMIRType(types::HeapTypeSet *types);
 
   public:
     TypeInferenceOracle() : cx(NULL), script_(NULL) {}
 
-    bool init(JSContext *cx, JSScript *script);
+    bool init(JSContext *cx, HandleScript script);
 
     UnrootedScript script() { return script_.get(); }
 
-    UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc);
-    BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc);
-    Unary unaryOp(JSScript *script, jsbytecode *pc);
-    Binary binaryOp(JSScript *script, jsbytecode *pc);
-    types::StackTypeSet *thisTypeSet(JSScript *script);
+    UnaryTypes unaryTypes(UnrootedScript script, jsbytecode *pc);
+    BinaryTypes binaryTypes(UnrootedScript script, jsbytecode *pc);
+    Unary unaryOp(UnrootedScript script, jsbytecode *pc);
+    Binary binaryOp(UnrootedScript script, jsbytecode *pc);
+    types::StackTypeSet *thisTypeSet(UnrootedScript script);
     bool getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes);
-    types::StackTypeSet *parameterTypeSet(JSScript *script, size_t index);
-    types::HeapTypeSet *globalPropertyTypeSet(JSScript *script, jsbytecode *pc, jsid id);
-    types::StackTypeSet *propertyRead(JSScript *script, jsbytecode *pc);
-    types::StackTypeSet *propertyReadBarrier(JSScript *script, jsbytecode *pc);
-    bool propertyReadIdempotent(JSScript *script, jsbytecode *pc, HandleId id);
-    bool propertyReadAccessGetter(JSScript *script, jsbytecode *pc);
-    types::HeapTypeSet *globalPropertyWrite(JSScript *script, jsbytecode *pc, jsid id, bool *canSpecialize);
-    types::StackTypeSet *returnTypeSet(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier);
-    types::StackTypeSet *getCallTarget(JSScript *caller, uint32_t argc, jsbytecode *pc);
-    types::StackTypeSet *getCallArg(JSScript *caller, uint32_t argc, uint32_t arg, jsbytecode *pc);
-    types::StackTypeSet *getCallReturn(JSScript *caller, jsbytecode *pc);
+    types::StackTypeSet *parameterTypeSet(UnrootedScript script, size_t index);
+    types::HeapTypeSet *globalPropertyTypeSet(UnrootedScript script, jsbytecode *pc, jsid id);
+    types::StackTypeSet *propertyRead(UnrootedScript script, jsbytecode *pc);
+    types::StackTypeSet *propertyReadBarrier(HandleScript script, jsbytecode *pc);
+    bool propertyReadIdempotent(HandleScript script, jsbytecode *pc, HandleId id);
+    bool propertyReadAccessGetter(UnrootedScript script, jsbytecode *pc);
+    types::HeapTypeSet *globalPropertyWrite(UnrootedScript script, jsbytecode *pc, jsid id, bool *canSpecialize);
+    types::StackTypeSet *returnTypeSet(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier);
+    types::StackTypeSet *getCallTarget(UnrootedScript caller, uint32_t argc, jsbytecode *pc);
+    types::StackTypeSet *getCallArg(UnrootedScript caller, uint32_t argc, uint32_t arg, jsbytecode *pc);
+    types::StackTypeSet *getCallReturn(UnrootedScript caller, jsbytecode *pc);
     bool inObjectIsDenseArray(HandleScript script, jsbytecode *pc);
-    bool inArrayIsPacked(JSScript *script, jsbytecode *pc);
-    bool elementReadIsDenseArray(JSScript *script, jsbytecode *pc);
-    bool elementReadIsTypedArray(JSScript *script, jsbytecode *pc, int *atype);
-    bool elementReadIsString(JSScript *script, jsbytecode *pc);
-    bool elementReadIsPacked(JSScript *script, jsbytecode *pc);
-    void elementReadGeneric(JSScript *script, jsbytecode *pc, bool *cacheable, bool *monitorResult);
+    bool inArrayIsPacked(UnrootedScript script, jsbytecode *pc);
+    bool elementReadIsDenseArray(UnrootedScript script, jsbytecode *pc);
+    bool elementReadIsTypedArray(UnrootedScript script, jsbytecode *pc, int *atype);
+    bool elementReadIsString(UnrootedScript script, jsbytecode *pc);
+    bool elementReadIsPacked(UnrootedScript script, jsbytecode *pc);
+    void elementReadGeneric(UnrootedScript script, jsbytecode *pc, bool *cacheable, bool *monitorResult);
     bool elementWriteIsDenseArray(HandleScript script, jsbytecode *pc);
-    bool elementWriteIsTypedArray(JSScript *script, jsbytecode *pc, int *arrayType);
-    bool elementWriteIsPacked(JSScript *script, jsbytecode *pc);
-    bool setElementHasWrittenHoles(JSScript *script, jsbytecode *pc);
+    bool elementWriteIsTypedArray(UnrootedScript script, jsbytecode *pc, int *arrayType);
+    bool elementWriteIsPacked(UnrootedScript script, jsbytecode *pc);
+    bool setElementHasWrittenHoles(UnrootedScript script, jsbytecode *pc);
     bool propertyWriteCanSpecialize(UnrootedScript script, jsbytecode *pc);
-    bool propertyWriteNeedsBarrier(JSScript *script, jsbytecode *pc, jsid id);
-    bool elementWriteNeedsBarrier(JSScript *script, jsbytecode *pc);
-    MIRType elementWrite(JSScript *script, jsbytecode *pc);
+    bool propertyWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc, jsid id);
+    bool elementWriteNeedsBarrier(UnrootedScript script, jsbytecode *pc);
+    MIRType elementWrite(UnrootedScript script, jsbytecode *pc);
     bool arrayPrototypeHasIndexedProperty();
     bool canInlineCalls();
-    bool canInlineCall(JSScript *caller, jsbytecode *pc);
+    bool canInlineCall(HandleScript caller, jsbytecode *pc);
     bool canEnterInlinedFunction(JSFunction *callee);
-    types::StackTypeSet *aliasedVarBarrier(JSScript *script, jsbytecode *pc, types::StackTypeSet **barrier);
+    types::StackTypeSet *aliasedVarBarrier(UnrootedScript script, jsbytecode *pc, types::StackTypeSet **barrier);
 
     LazyArgumentsType isArgumentObject(types::StackTypeSet *obj);
-    LazyArgumentsType propertyReadMagicArguments(JSScript *script, jsbytecode *pc);
-    LazyArgumentsType elementReadMagicArguments(JSScript *script, jsbytecode *pc);
-    LazyArgumentsType elementWriteMagicArguments(JSScript *script, jsbytecode *pc);
+    LazyArgumentsType propertyReadMagicArguments(UnrootedScript script, jsbytecode *pc);
+    LazyArgumentsType elementReadMagicArguments(UnrootedScript script, jsbytecode *pc);
+    LazyArgumentsType elementWriteMagicArguments(UnrootedScript script, jsbytecode *pc);
 };
 
 static inline MIRType
 MIRTypeFromValueType(JSValueType type)
 {
     switch (type) {
       case JSVAL_TYPE_DOUBLE:
         return MIRType_Double;
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -51,17 +51,17 @@ InvokeFunction(JSContext *cx, JSFunction
     Value fval = ObjectValue(*fun);
 
     // In order to prevent massive bouncing between Ion and JM, see if we keep
     // hitting functions that are uncompilable.
     if (fun->isInterpreted()) {
         if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
             return false;
         if (!fun->nonLazyScript()->canIonCompile()) {
-            JSScript *script = GetTopIonJSScript(cx);
+            UnrootedScript script = GetTopIonJSScript(cx);
             if (script->hasIonScript() &&
                 ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit)
             {
                 AutoFlushCache afc("InvokeFunction");
 
                 // Poison the script so we don't try to run it again. This will
                 // trigger invalidation.
                 ForbidCompilation(cx, script);
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -214,17 +214,17 @@ template <> struct TypeToArgProperties<H
 };
 template <> struct TypeToArgProperties<HandlePropertyName> {
     static const uint32_t result = TypeToArgProperties<PropertyName *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleFunction> {
     static const uint32_t result = TypeToArgProperties<JSFunction *>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleScript> {
-    static const uint32_t result = TypeToArgProperties<JSScript *>::result | VMFunction::ByRef;
+    static const uint32_t result = TypeToArgProperties<RawScript>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleValue> {
     static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<MutableHandleValue> {
     static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef;
 };
 template <> struct TypeToArgProperties<HandleShape> {
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1100,17 +1100,17 @@ void
 CodeGeneratorARM::linkAbsoluteLabels()
 {
     // arm doesn't have deferred doubles, so this whole thing should be a NOP (right?)
     // deferred doubles are an x86 mechanism for loading doubles into registers by storing
     // them after the function body, then referring to them by their absolute address.
     // On arm, everything should just go in a pool.
 # if 0
     JS_NOT_REACHED("Absolute Labels NYI");
-    JSScript *script = gen->info().script();
+    UnrootedScript script = gen->info().script();
     IonCode *method = script->ion->method();
 
     for (size_t i = 0; i < deferredDoubles_.length(); i++) {
         DeferredDouble *d = deferredDoubles_[i];
         const Value &v = script->ion->getConstant(d->index());
         MacroAssembler::Bind(method, d->label(), &v);
     }
 #endif
--- a/js/src/ion/shared/CodeGenerator-shared.h
+++ b/js/src/ion/shared/CodeGenerator-shared.h
@@ -328,24 +328,24 @@ class OutOfLineCode : public TempObject
         return &rejoin_;
     }
     void setFramePushed(uint32_t framePushed) {
         framePushed_ = framePushed;
     }
     uint32_t framePushed() const {
         return framePushed_;
     }
-    void setSource(JSScript *script, jsbytecode *pc) {
+    void setSource(UnrootedScript script, jsbytecode *pc) {
         script_ = script;
         pc_ = pc;
     }
     jsbytecode *pc() {
         return pc_;
     }
-    JSScript *script() {
+    UnrootedScript script() {
         return script_;
     }
 };
 
 // For OOL paths that want a specific-typed code generator.
 template <typename T>
 class OutOfLineCodeBase : public OutOfLineCode
 {
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -132,17 +132,17 @@ CodeGeneratorX86::visitUnbox(LUnbox *unb
             return false;
     }
     return true;
 }
 
 void
 CodeGeneratorX86::linkAbsoluteLabels()
 {
-    JSScript *script = gen->info().script();
+    UnrootedScript script = gen->info().script();
     IonCode *method = script->ion->method();
 
     for (size_t i = 0; i < deferredDoubles_.length(); i++) {
         DeferredDouble *d = deferredDoubles_[i];
         const Value &v = script->ion->getConstant(d->index());
         MacroAssembler::Bind(method, d->label(), &v);
     }
 }
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -3,17 +3,16 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsalloc_h_
 #define jsalloc_h_
 
-#include "jspubtd.h"
 #include "jsutil.h"
 
 namespace js {
 
 /*
  * Allocation policies.  These model the concept:
  *  - public copy constructor, assignment, destructor
  *  - void *malloc_(size_t)
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -17,20 +17,19 @@ using namespace js;
 using namespace js::analyze;
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode
 /////////////////////////////////////////////////////////////////////
 
 #ifdef DEBUG
 void
-analyze::PrintBytecode(JSContext *cx, JSScript *scriptArg, jsbytecode *pc)
+analyze::PrintBytecode(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
-    RootedScript script(cx, scriptArg);
-
+    AssertCanGC();
     printf("#%u:", script->id());
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return;
     js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
 }
 #endif
@@ -1960,17 +1959,17 @@ ScriptAnalysis::needsArgsObj(JSContext *
 }
 
 CrossSSAValue
 CrossScriptSSA::foldValue(const CrossSSAValue &cv)
 {
     const Frame &frame = getFrame(cv.frame);
     const SSAValue &v = cv.v;
 
-    JSScript *parentScript = NULL;
+    UnrootedScript parentScript = NULL;
     ScriptAnalysis *parentAnalysis = NULL;
     if (frame.parent != INVALID_FRAME) {
         parentScript = getFrame(frame.parent).script;
         parentAnalysis = parentScript->analysis();
     }
 
     if (v.kind() == SSAValue::VAR && v.varInitial() && parentScript) {
         uint32_t slot = v.varSlot();
@@ -1993,17 +1992,17 @@ CrossScriptSSA::foldValue(const CrossSSA
             }
             break;
 
           case JSOP_CALL: {
             /*
              * If there is a single inline callee with a single return site,
              * propagate back to that.
              */
-            JSScript *callee = NULL;
+            UnrootedScript callee = NULL;
             uint32_t calleeFrame = INVALID_FRAME;
             for (unsigned i = 0; i < numFrames(); i++) {
                 if (iterFrame(i).parent == cv.frame && iterFrame(i).parentpc == pc) {
                     if (callee)
                         return cv;  /* Multiple callees */
                     callee = iterFrame(i).script;
                     calleeFrame = iterFrame(i).index;
                 }
@@ -2045,24 +2044,25 @@ CrossScriptSSA::foldValue(const CrossSSA
 
 void
 ScriptAnalysis::printSSA(JSContext *cx)
 {
     AutoEnterAnalysis enter(cx);
 
     printf("\n");
 
+    RootedScript script(cx, script_);
     for (unsigned offset = 0; offset < script_->length; offset++) {
         Bytecode *code = maybeCode(offset);
         if (!code)
             continue;
 
         jsbytecode *pc = script_->code + offset;
 
-        PrintBytecode(cx, script_, pc);
+        PrintBytecode(cx, script, pc);
 
         SlotValue *newv = code->newValues;
         if (newv) {
             while (newv->slot) {
                 if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
                     newv++;
                     continue;
                 }
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -168,17 +168,17 @@ class Bytecode
     /* Types for all values pushed by this bytecode. */
     types::StackTypeSet *pushedTypes;
 
     /* Any type barriers in place at this bytecode. */
     types::TypeBarrier *typeBarriers;
 };
 
 static inline unsigned
-GetDefCount(JSScript *script, unsigned offset)
+GetDefCount(UnrootedScript script, unsigned offset)
 {
     JS_ASSERT(offset < script->length);
     jsbytecode *pc = script->code + offset;
 
     /*
      * Add an extra pushed value for OR/AND opcodes, so that they are included
      * in the pushed array of stack values for type inference.
      */
@@ -197,17 +197,17 @@ GetDefCount(JSScript *script, unsigned o
          */
         return (pc[1] + 1);
       default:
         return StackDefs(script, pc);
     }
 }
 
 static inline unsigned
-GetUseCount(JSScript *script, unsigned offset)
+GetUseCount(UnrootedScript script, unsigned offset)
 {
     JS_ASSERT(offset < script->length);
     jsbytecode *pc = script->code + offset;
 
     if (JSOp(*pc) == JSOP_PICK)
         return (pc[1] + 1);
     if (js_CodeSpec[*pc].nuses == -1)
         return StackUses(script, pc);
@@ -326,17 +326,17 @@ NegateCompareOp(JSOp op)
         return JSOP_STRICTNE;
       default:
         JS_NOT_REACHED("unrecognized op");
         return op;
     }
 }
 
 static inline unsigned
-FollowBranch(JSContext *cx, JSScript *script, unsigned offset)
+FollowBranch(JSContext *cx, UnrootedScript script, unsigned offset)
 {
     /*
      * Get the target offset of a branch. For GOTO opcodes implementing
      * 'continue' statements, short circuit any artificial backwards jump
      * inserted by the emitter.
      */
     jsbytecode *pc = script->code + offset;
     unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
@@ -354,28 +354,28 @@ static inline uint32_t CalleeSlot() {
     return 0;
 }
 static inline uint32_t ThisSlot() {
     return 1;
 }
 static inline uint32_t ArgSlot(uint32_t arg) {
     return 2 + arg;
 }
-static inline uint32_t LocalSlot(JSScript *script, uint32_t local) {
+static inline uint32_t LocalSlot(UnrootedScript script, uint32_t local) {
     return 2 + (script->function() ? script->function()->nargs : 0) + local;
 }
-static inline uint32_t TotalSlots(JSScript *script) {
+static inline uint32_t TotalSlots(UnrootedScript script) {
     return LocalSlot(script, 0) + script->nfixed;
 }
 
-static inline uint32_t StackSlot(JSScript *script, uint32_t index) {
+static inline uint32_t StackSlot(UnrootedScript script, uint32_t index) {
     return TotalSlots(script) + index;
 }
 
-static inline uint32_t GetBytecodeSlot(JSScript *script, jsbytecode *pc)
+static inline uint32_t GetBytecodeSlot(UnrootedScript script, jsbytecode *pc)
 {
     switch (JSOp(*pc)) {
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_SETARG:
         return ArgSlot(GET_SLOTNO(pc));
 
@@ -541,17 +541,17 @@ struct LifetimeVariable
         }
         return UINT32_MAX;
     }
     uint32_t firstWrite(LoopAnalysis *loop) const {
         return firstWrite(loop->head, loop->backedge);
     }
 
     /* Return true if the variable cannot decrease during the body of a loop. */
-    bool nonDecreasing(JSScript *script, LoopAnalysis *loop) const {
+    bool nonDecreasing(UnrootedScript script, LoopAnalysis *loop) const {
         Lifetime *segment = lifetime ? lifetime : saved;
         while (segment && segment->start <= loop->backedge) {
             if (segment->start >= loop->head && segment->write) {
                 switch (JSOp(script->code[segment->start])) {
                   case JSOP_INCLOCAL:
                   case JSOP_LOCALINC:
                   case JSOP_INCARG:
                   case JSOP_ARGINC:
@@ -857,17 +857,17 @@ class ScriptAnalysis
     uint32_t numReturnSites_;
 
     /* --------- Lifetime analysis --------- */
 
     LifetimeVariable *lifetimes;
 
   public:
 
-    ScriptAnalysis(JSScript *script) {
+    ScriptAnalysis(UnrootedScript script) {
         PodZero(this);
         this->script_ = script;
 #ifdef DEBUG
         this->originalDebugMode_ = script_->compartment()->debugMode();
 #endif
     }
 
     bool ranBytecode() { return ranBytecode_; }
@@ -1259,69 +1259,71 @@ class CrossScriptSSA
 
     struct Frame {
         uint32_t index;
         JSScript *script;
         uint32_t depth;  /* Distance from outer frame to this frame, in sizeof(Value) */
         uint32_t parent;
         jsbytecode *parentpc;
 
-        Frame(uint32_t index, JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc)
-            : index(index), script(script), depth(depth), parent(parent), parentpc(parentpc)
+        Frame(uint32_t index, UnrootedScript script, uint32_t depth, uint32_t parent,
+              jsbytecode *parentpc)
+          : index(index), script(script), depth(depth), parent(parent), parentpc(parentpc)
         {}
     };
 
     const Frame &getFrame(uint32_t index) {
         if (index == OUTER_FRAME)
             return outerFrame;
         return inlineFrames[index];
     }
 
     unsigned numFrames() { return 1 + inlineFrames.length(); }
     const Frame &iterFrame(unsigned i) {
         if (i == 0)
             return outerFrame;
         return inlineFrames[i - 1];
     }
 
-    JSScript *outerScript() { return outerFrame.script; }
+    UnrootedScript outerScript() { return outerFrame.script; }
 
     /* Total length of scripts preceding a frame. */
     size_t frameLength(uint32_t index) {
         if (index == OUTER_FRAME)
             return 0;
         size_t res = outerFrame.script->length;
         for (unsigned i = 0; i < index; i++)
             res += inlineFrames[i].script->length;
         return res;
     }
 
     types::StackTypeSet *getValueTypes(const CrossSSAValue &cv) {
         return getFrame(cv.frame).script->analysis()->getValueTypes(cv.v);
     }
 
-    bool addInlineFrame(JSScript *script, uint32_t depth, uint32_t parent, jsbytecode *parentpc)
+    bool addInlineFrame(UnrootedScript script, uint32_t depth, uint32_t parent,
+                        jsbytecode *parentpc)
     {
         uint32_t index = inlineFrames.length();
         return inlineFrames.append(Frame(index, script, depth, parent, parentpc));
     }
 
-    CrossScriptSSA(JSContext *cx, JSScript *outer)
+    CrossScriptSSA(JSContext *cx, UnrootedScript outer)
         : outerFrame(OUTER_FRAME, outer, 0, INVALID_FRAME, NULL), inlineFrames(cx)
     {}
 
     CrossSSAValue foldValue(const CrossSSAValue &cv);
 
   private:
     Frame outerFrame;
     Vector<Frame> inlineFrames;
 };
 
 #ifdef DEBUG
-void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc);
+void PrintBytecode(JSContext *cx, HandleScript script, jsbytecode *pc);
 #endif
 
 } /* namespace analyze */
 } /* namespace js */
 
 namespace js {
 namespace tl {
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -7136,17 +7136,17 @@ JS::AssertArgumentsAreSane(JSContext *cx
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 }
 #endif /* DEBUG */
 
 JS_PUBLIC_API(void *)
-JS_EncodeScript(JSContext *cx, JSRawScript scriptArg, uint32_t *lengthp)
+JS_EncodeScript(JSContext *cx, RawScript scriptArg, uint32_t *lengthp)
 {
     XDREncoder encoder(cx);
     RootedScript script(cx, scriptArg);
     if (!encoder.codeScript(&script))
         return NULL;
     return encoder.forgetData(lengthp);
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -511,17 +511,17 @@ static bool
 checkReportFlags(JSContext *cx, unsigned *flags)
 {
     if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
         /*
          * Error in strict code; warning with strict option; okay otherwise.
          * We assume that if the top frame is a native, then it is strict if
          * the nearest scripted frame is strict, see bug 536306.
          */
-        JSScript *script = cx->stack.currentScript();
+        UnrootedScript script = cx->stack.currentScript();
         if (script && script->strict)
             *flags &= ~JSREPORT_WARNING;
         else if (cx->hasStrictOption())
             *flags |= JSREPORT_WARNING;
         else
             return true;
     } else if (JSREPORT_IS_STRICT(*flags)) {
         /* Warning/error only when JSOPTION_STRICT is set. */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -199,20 +199,20 @@ struct EvalCacheLookup
     JSCompartment *compartment;
 };
 
 struct EvalCacheHashPolicy
 {
     typedef EvalCacheLookup Lookup;
 
     static HashNumber hash(const Lookup &l);
-    static bool match(JSScript *script, const EvalCacheLookup &l);
+    static bool match(UnrootedScript script, const EvalCacheLookup &l);
 };
 
-typedef HashSet<JSScript *, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
+typedef HashSet<RawScript, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
 
 class NativeIterCache
 {
     static const size_t SIZE = size_t(1) << 8;
 
     /* Cached native iterators. */
     PropertyIteratorObject *data[SIZE];
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -420,17 +420,17 @@ js::XDRInterpretedFunction(XDRState<mode
         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->nonLazyScript()->bindings.numArgs());
         RootedScript script(cx, fun->nonLazyScript());
-        js_CallNewScriptHook(cx, script, fun);
+        CallNewScriptHook(cx, script, fun);
         objp.set(fun);
     }
 
     return true;
 }
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, MutableHandleObject);
@@ -446,30 +446,30 @@ js::CloneInterpretedFunction(JSContext *
     /* NB: Keep this in sync with XDRInterpretedFunction. */
 
     RootedFunction clone(cx, js_NewFunction(cx, NullPtr(), NULL, 0,
                                             JSFunction::INTERPRETED, NullPtr(), NullPtr()));
     if (!clone)
         return NULL;
 
     RootedScript srcScript(cx, srcFun->nonLazyScript());
-    RawScript clonedScript = CloneScript(cx, enclosingScope, clone, srcScript);
+    RootedScript clonedScript(cx, 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;
 
     RootedScript cloneScript(cx, clone->nonLazyScript());
-    js_CallNewScriptHook(cx, cloneScript, clone);
+    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.
  */
@@ -1515,27 +1515,27 @@ js_CloneFunctionObject(JSContext *cx, Ha
             JS_ASSERT(script->compartment() == fun->compartment());
             JS_ASSERT_IF(script->compartment() != cx->compartment,
                          !script->enclosingStaticScope());
 
             RootedObject scope(cx, script->enclosingStaticScope());
 
             clone->mutableScript().init(NULL);
 
-            RawScript cscript = CloneScript(cx, scope, clone, script);
+            RootedScript cscript(cx, CloneScript(cx, scope, clone, script));
             if (!cscript)
                 return NULL;
 
             clone->setScript(cscript);
             cscript->setFunction(clone);
 
             GlobalObject *global = script->compileAndGo ? &script->global() : NULL;
 
             script = clone->nonLazyScript();
-            js_CallNewScriptHook(cx, script, clone);
+            CallNewScriptHook(cx, script, clone);
             Debugger::onNewScript(cx, script, global);
         }
     }
     return clone;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4566,17 +4566,17 @@ js::StopPCCountProfiling(JSContext *cx)
     ReleaseAllJITCode(rt->defaultFreeOp());
 
     ScriptAndCountsVector *vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
     if (!vec)
         return;
 
     for (CompartmentsIter c(rt); !c.done(); c.next()) {
         for (CellIter i(c, FINALIZE_SCRIPT); !i.done(); i.next()) {
-            JSScript *script = i.get<JSScript>();
+            RawScript script = i.get<JSScript>();
             if (script->hasScriptCounts && script->types) {
                 ScriptAndCounts sac;
                 sac.script = script;
                 sac.scriptCounts.set(script->releaseScriptCounts());
                 if (!vec->append(sac))
                     sac.scriptCounts.destroy(rt->defaultFreeOp());
             }
         }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -523,31 +523,31 @@ enum PropertyAccessKind {
     PROPERTY_READ,
     PROPERTY_READ_EXISTING
 };
 
 /* Constraints for reads/writes on object properties. */
 template <PropertyAccessKind access>
 class TypeConstraintProp : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     jsbytecode *pc;
 
     /*
      * If assign is true, the target is used to update a property of the object.
      * If assign is false, the target is assigned the value of the property.
      */
     StackTypeSet *target;
 
     /* Property being accessed. */
     jsid id;
 
-    TypeConstraintProp(JSScript *script, jsbytecode *pc, StackTypeSet *target, jsid id)
+    TypeConstraintProp(UnrootedScript script, jsbytecode *pc, StackTypeSet *target, jsid id)
         : script_(script), pc(pc), target(target), id(id)
     {
         JS_ASSERT(script && pc && target);
     }
 
     const char *kind() { return "prop"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
@@ -590,25 +590,25 @@ HeapTypeSet::addGetProperty(JSContext *c
  * These are derived from the types on the properties themselves, rather than
  * those pushed in the 'this' slot at the call site, which allows us to retain
  * correlations between the type of the 'this' object and the associated
  * callee scripts at polymorphic call sites.
  */
 template <PropertyAccessKind access>
 class TypeConstraintCallProp : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     jsbytecode *callpc;
 
     /* Property being accessed. */
     jsid id;
 
-    TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
+    TypeConstraintCallProp(UnrootedScript script, jsbytecode *callpc, jsid id)
         : script_(script), callpc(callpc), id(id)
     {
         JS_ASSERT(script && callpc);
     }
 
     const char *kind() { return "callprop"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
@@ -635,25 +635,25 @@ HeapTypeSet::addCallProperty(JSContext *
 /*
  * Constraints for generating 'set' property constraints on a SETELEM only if
  * the element type may be a number. For SETELEM we only account for integer
  * indexes, and if the element cannot be an integer (e.g. it must be a string)
  * then we lose precision by treating it like one.
  */
 class TypeConstraintSetElement : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     jsbytecode *pc;
 
     StackTypeSet *objectTypes;
     StackTypeSet *valueTypes;
 
-    TypeConstraintSetElement(JSScript *script, jsbytecode *pc,
+    TypeConstraintSetElement(UnrootedScript script, jsbytecode *pc,
                              StackTypeSet *objectTypes, StackTypeSet *valueTypes)
         : script_(script), pc(pc),
           objectTypes(objectTypes), valueTypes(valueTypes)
     {
         JS_ASSERT(script && pc);
     }
 
     const char *kind() { return "setelement"; }
@@ -693,28 +693,28 @@ void
 StackTypeSet::addCall(JSContext *cx, TypeCallsite *site)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintCall>(site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     jsbytecode *pc;
 
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
 
     /* For addition operations, the other operand. */
     TypeSet *other;
 
-    TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
+    TypeConstraintArith(UnrootedScript script, jsbytecode *pc, TypeSet *target, TypeSet *other)
         : script_(script), pc(pc), target(target), other(other)
     {
         JS_ASSERT(target);
     }
 
     const char *kind() { return "arith"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
@@ -725,22 +725,22 @@ StackTypeSet::addArith(JSContext *cx, Ha
                        TypeSet *other)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     TypeSet *target;
 
-    TypeConstraintTransformThis(JSScript *script, TypeSet *target)
+    TypeConstraintTransformThis(UnrootedScript script, TypeSet *target)
         : script_(script), target(target)
     {}
 
     const char *kind() { return "transformthis"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
@@ -751,24 +751,24 @@ StackTypeSet::addTransformThis(JSContext
 }
 
 /*
  * Constraint which adds a particular type to the 'this' types of all
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
     jsbytecode *callpc;
     Type type;
     StackTypeSet *types;
 
-    TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, StackTypeSet *types)
+    TypeConstraintPropagateThis(UnrootedScript script, jsbytecode *callpc, Type type, StackTypeSet *types)
         : script_(script), callpc(callpc), type(type), types(types)
     {}
 
     const char *kind() { return "propagatethis"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
@@ -913,30 +913,31 @@ void ScriptAnalysis::breakTypeBarriersSS
 
 /*
  * Subset constraint for property reads and argument passing which can add type
  * barriers on the read instead of passing types along.
  */
 class TypeConstraintSubsetBarrier : public TypeConstraint
 {
   public:
-    JSScript *script;
+    RawScript script;
     jsbytecode *pc;
     TypeSet *target;
 
-    TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
+    TypeConstraintSubsetBarrier(UnrootedScript script, jsbytecode *pc, TypeSet *target)
         : script(script), pc(pc), target(target)
     {}
 
     const char *kind() { return "subsetBarrier"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         if (!target->hasType(type)) {
-            if (!script->ensureRanAnalysis(cx))
+            RootedScript scriptRoot(cx, script);
+            if (!JSScript::ensureRanAnalysis(cx, scriptRoot))
                 return;
             script->analysis()->addTypeBarrier(cx, pc, target, type);
         }
     }
 };
 
 void
 StackTypeSet::addSubsetBarrier(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target)
@@ -2085,20 +2086,20 @@ AddPendingRecompile(JSContext *cx, Handl
 
 /*
  * As for TypeConstraintFreeze, but describes an implicit freeze constraint
  * added for stack types within a script. Applies to all compilations of the
  * script, not just a single one.
  */
 class TypeConstraintFreezeStack : public TypeConstraint
 {
-    JSScript *script_;
+    RawScript script_;
 
   public:
-    TypeConstraintFreezeStack(JSScript *script)
+    TypeConstraintFreezeStack(UnrootedScript script)
         : script_(script)
     {}
 
     const char *kind() { return "freezeStack"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         /*
@@ -2392,17 +2393,17 @@ types::UseNewTypeForInitializer(JSContex
     if (!cx->typeInferenceEnabled() || script->function())
         return false;
 
     if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
         return false;
 
     AutoEnterTypeInference enter(cx);
 
-    if (!script->ensureRanAnalysis(cx))
+    if (!JSScript::ensureRanAnalysis(cx, script))
         return false;
 
     return !script->analysis()->getCode(pc).inLoop;
 }
 
 bool
 types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script)
 {
@@ -2642,19 +2643,19 @@ TypeCompartment::addPendingRecompile(JSC
 # endif
 #endif
 }
 
 void
 TypeCompartment::monitorBytecode(JSContext *cx, HandleScript script, uint32_t offset,
                                  bool returnOnly)
 {
-    AutoAssertNoGC nogc;
-
-    if (!script->ensureRanInference(cx))
+    AssertCanGC();
+
+    if (!JSScript::ensureRanInference(cx, script))
         return;
 
     ScriptAnalysis *analysis = script->analysis();
     jsbytecode *pc = script->code + offset;
 
     JS_ASSERT_IF(returnOnly, js_CodeSpec[*pc].format & JOF_INVOKE);
 
     Bytecode &code = analysis->getCode(pc);
@@ -4637,16 +4638,18 @@ AnalyzePoppedThis(JSContext *cx, Vector<
                   TypeObject *type, JSFunction *fun, MutableHandleObject pbaseobj,
                   Vector<TypeNewScript::Initializer> *initializerList);
 
 static bool
 AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun,
                            MutableHandleObject pbaseobj,
                            Vector<TypeNewScript::Initializer> *initializerList)
 {
+    AssertCanGC();
+
     /*
      * When invoking 'new' on the specified script, try to find some properties
      * which will definitely be added to the created object before it has a
      * chance to escape and be accessed elsewhere.
      *
      * Returns true if the entire script was analyzed (pbaseobj has been
      * preserved), false if we had to bail out part way through (pbaseobj may
      * have been cleared).
@@ -4656,17 +4659,17 @@ AnalyzeNewScriptProperties(JSContext *cx
         /*
          * Bail out on really long initializer lists (far longer than maximum
          * number of properties we can track), we may be recursing.
          */
         return false;
     }
 
     RootedScript script(cx, fun->nonLazyScript());
-    if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) {
+    if (!JSScript::ensureRanAnalysis(cx, script) || !JSScript::ensureRanInference(cx, script)) {
         pbaseobj.set(NULL);
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
     ScriptAnalysis *analysis = script->analysis();
 
     /*
@@ -5126,23 +5129,24 @@ ScriptAnalysis::printTypes(JSContext *cx
     for (unsigned i = 0; i < script_->nfixed; i++) {
         if (!trackSlot(LocalSlot(script_, i))) {
             printf("\n    local%u:", i);
             TypeScript::LocalTypes(script_, i)->print();
         }
     }
     printf("\n");
 
+    RootedScript script(cx, script_);
     for (unsigned offset = 0; offset < script_->length; offset++) {
         if (!maybeCode(offset))
             continue;
 
         jsbytecode *pc = script_->code + offset;
 
-        PrintBytecode(cx, script_, pc);
+        PrintBytecode(cx, script, pc);
 
         if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
             continue;
 
         if (js_CodeSpec[*pc].format & JOF_TYPESET) {
             TypeSet *types = script_->analysis()->bytecodeTypes(pc);
             printf("  typeset %d:", (int) (types - script_->types->typeArray()));
             types->print();
@@ -5277,17 +5281,17 @@ IsAboutToBeFinalized(TypeObjectKey *key)
 void
 types::TypeDynamicResult(JSContext *cx, HandleScript script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     AutoEnterTypeInference enter(cx);
 
     /* Directly update associated type sets for applicable bytecodes. */
     if (js_CodeSpec[*pc].format & JOF_TYPESET) {
-        if (!script->ensureRanAnalysis(cx)) {
+        if (!JSScript::ensureRanAnalysis(cx, script)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         TypeSet *types = script->analysis()->bytecodeTypes(pc);
         if (!types->hasType(type)) {
             InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
                       script->id(), pc - script->code, TypeString(type));
             types->addType(cx, type);
@@ -5380,17 +5384,17 @@ void
 types::TypeMonitorResult(JSContext *cx, HandleScript script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     AutoEnterTypeInference enter(cx);
 
-    if (!script->ensureRanAnalysis(cx)) {
+    if (!JSScript::ensureRanAnalysis(cx, script)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     Type type = GetValueType(cx, rval);
     TypeSet *types = script->analysis()->bytecodeTypes(pc);
     if (types->hasType(type))
         return;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -8,24 +8,25 @@
 
 #ifndef jsinfer_h___
 #define jsinfer_h___
 
 #include "mozilla/Attributes.h"
 
 #include "jsalloc.h"
 #include "jsfriendapi.h"
-#include "jsprvtd.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
+ForwardDeclareJS(Script);
+
 namespace JS {
 struct TypeInferenceSizes;
 }
 
 namespace js {
 
 class TaggedProto
 {
@@ -1100,17 +1101,17 @@ struct TypeCallsite
     StackTypeSet **argumentTypes;
 
     /* Types of the this variable. */
     StackTypeSet *thisTypes;
 
     /* Type set receiving the return value of this call. */
     StackTypeSet *returnTypes;
 
-    inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
+    inline TypeCallsite(JSContext *cx, UnrootedScript script, jsbytecode *pc,
                         bool isNew, unsigned argumentCount);
 };
 
 /* Persistent type information for a script, retained across GCs. */
 class TypeScript
 {
     friend struct ::JSScript;
 
@@ -1126,17 +1127,17 @@ class TypeScript
      * Generated the first time the script is analyzed by inference and kept
      * after analysis purges.
      */
     HeapTypeSet *propertyReadTypes;
 
     /* Array of type type sets for variables and JOF_TYPESET ops. */
     TypeSet *typeArray() { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
 
-    static inline unsigned NumTypeSets(RawScript script);
+    static inline unsigned NumTypeSets(UnrootedScript script);
 
     static inline HeapTypeSet  *ReturnTypes(RawScript script);
     static inline StackTypeSet *ThisTypes(RawScript script);
     static inline StackTypeSet *ArgTypes(RawScript script, unsigned i);
     static inline StackTypeSet *LocalTypes(RawScript script, unsigned i);
 
     /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
     static inline StackTypeSet *SlotTypes(RawScript script, unsigned slot);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -410,17 +410,17 @@ struct AutoEnterCompilation
       : cx(cx),
         info(cx->compartment->types.compiledInfo),
         kind(kind)
     {
         JS_ASSERT(cx->compartment->activeAnalysis);
         JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning);
     }
 
-    bool init(JSScript *script, bool constructing, unsigned chunkIndex)
+    bool init(UnrootedScript script, bool constructing, unsigned chunkIndex)
     {
         CompilerOutput co;
         co.script = script;
         co.setKind(kind);
         co.constructing = constructing;
         co.barriers = cx->compartment->compileBarriers();
         co.chunkIndex = chunkIndex;
 
@@ -529,17 +529,17 @@ void TypeMonitorCallSlow(JSContext *cx, 
 inline bool
 TypeMonitorCall(JSContext *cx, const js::CallArgs &args, bool constructing)
 {
     js::RootedObject callee(cx, &args.callee());
     if (callee->isFunction()) {
         JSFunction *fun = callee->toFunction();
         if (fun->isInterpreted()) {
             js::RootedScript script(cx, fun->nonLazyScript());
-            if (!script->ensureRanAnalysis(cx))
+            if (!JSScript::ensureRanAnalysis(cx, script))
                 return false;
             if (cx->typeInferenceEnabled())
                 TypeMonitorCallSlow(cx, callee, args, constructing);
         }
     }
 
     return true;
 }
@@ -657,17 +657,17 @@ FixArrayType(JSContext *cx, HandleObject
 
 inline void
 FixObjectType(JSContext *cx, HandleObject obj)
 {
     if (cx->typeInferenceEnabled())
         cx->compartment->types.fixObjectType(cx, obj);
 }
 
-/* Interface helpers for JSScript */
+/* Interface helpers for RawScript */
 extern void TypeMonitorResult(JSContext *cx, HandleScript script, jsbytecode *pc,
                               const js::Value &rval);
 extern void TypeDynamicResult(JSContext *cx, HandleScript script, jsbytecode *pc,
                               js::types::Type type);
 
 inline bool
 UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
 {
@@ -735,68 +735,74 @@ UseNewTypeForClone(JSFunction *fun)
     return hasArguments && hasApply;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline unsigned
-TypeScript::NumTypeSets(RawScript script)
+TypeScript::NumTypeSets(UnrootedScript script)
 {
     return script->nTypeSets + analyze::TotalSlots(script);
 }
 
 /* static */ inline HeapTypeSet *
 TypeScript::ReturnTypes(RawScript script)
 {
+    AutoAssertNoGC nogc;
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
     return types->toHeapTypeSet();
 }
 
 /* static */ inline StackTypeSet *
 TypeScript::ThisTypes(RawScript script)
 {
+    AutoAssertNoGC nogc;
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
     return types->toStackTypeSet();
 }
 
 /*
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
 /* static */ inline StackTypeSet *
 TypeScript::ArgTypes(RawScript script, unsigned i)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(i < script->function()->nargs);
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
     return types->toStackTypeSet();
 }
 
 /* static */ inline StackTypeSet *
 TypeScript::LocalTypes(RawScript script, unsigned i)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(i < script->nfixed);
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
     return types->toStackTypeSet();
 }
 
 /* static */ inline StackTypeSet *
 TypeScript::SlotTypes(RawScript script, unsigned slot)
 {
+    AutoAssertNoGC nogc;
     JS_ASSERT(slot < js::analyze::TotalSlots(script));
     TypeSet *types = script->types->typeArray() + script->nTypeSets + slot;
     return types->toStackTypeSet();
 }
 
 /* static */ inline TypeObject *
 TypeScript::StandardType(JSContext *cx, HandleScript script, JSProtoKey key)
 {
+    AssertCanGC();
     js::RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto, NULL))
         return NULL;
     return proto->getNewType(cx);
 }
 
 struct AllocationSiteKey {
     JSScript *script;
@@ -985,17 +991,17 @@ TypeScript::SetThis(JSContext *cx, Handl
     if (!ThisTypes(script)->hasType(type) || analyze) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setThis #%u: %s",
                   script->id(), TypeString(type));
         ThisTypes(script)->addType(cx, type);
 
         if (analyze)
-            script->ensureRanInference(cx);
+            JSScript::ensureRanInference(cx, script);
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, HandleScript script, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
         SetThis(cx, script, GetValueType(cx, value));
@@ -1473,17 +1479,17 @@ TypeSet::getTypeObject(unsigned i) const
     return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCallsite
 /////////////////////////////////////////////////////////////////////
 
 inline
-TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
+TypeCallsite::TypeCallsite(JSContext *cx, UnrootedScript script, jsbytecode *pc,
                            bool isNew, unsigned argumentCount)
     : script(script), pc(pc), isNew(isNew), argumentCount(argumentCount),
       thisTypes(NULL), returnTypes(NULL)
 {
     /* Caller must check for failure. */
     argumentTypes = cx->analysisLifoAlloc().newArray<StackTypeSet*>(argumentCount);
 }
 
@@ -1710,41 +1716,41 @@ Property::Property(const Property &o)
 } } /* namespace js::types */
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }
 
-inline bool
-JSScript::ensureRanAnalysis(JSContext *cx)
+/* static */ inline bool
+JSScript::ensureRanAnalysis(JSContext *cx, JS::HandleScript script)
 {
+    AssertCanGC();
     js::analyze::AutoEnterAnalysis aea(cx->compartment);
-    js::RootedScript self(cx, this);
 
-    if (!self->ensureHasTypes(cx))
+    if (!script->ensureHasTypes(cx))
         return false;
-    if (!self->hasAnalysis() && !self->makeAnalysis(cx))
+    if (!script->hasAnalysis() && !script->makeAnalysis(cx))
         return false;
-    JS_ASSERT(self->analysis()->ranBytecode());
+    JS_ASSERT(script->analysis()->ranBytecode());
     return true;
 }
 
-inline bool
-JSScript::ensureRanInference(JSContext *cx)
+/* static */ inline bool
+JSScript::ensureRanInference(JSContext *cx, JS::HandleScript script)
 {
-    js::RootedScript self(cx, this);
-    if (!ensureRanAnalysis(cx))
+    AssertCanGC();
+    if (!script->ensureRanAnalysis(cx, script))
         return false;
-    if (!self->analysis()->ranInference()) {
+    if (!script->analysis()->ranInference()) {
         js::types::AutoEnterTypeInference enter(cx);
-        self->analysis()->analyzeTypes(cx);
+        script->analysis()->analyzeTypes(cx);
     }
-    return !self->analysis()->OOM() &&
+    return !script->analysis()->OOM() &&
         !cx->compartment->types.pendingNukeTypes;
 }
 
 inline bool
 JSScript::hasAnalysis()
 {
     return types && types->analysis;
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -522,17 +522,17 @@ js::ExecuteKernel(JSContext *cx, HandleS
             result->setUndefined();
         return true;
     }
 
     ExecuteFrameGuard efg;
     if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
         return false;
 
-    if (!script->ensureRanAnalysis(cx))
+    if (!JSScript::ensureRanAnalysis(cx, script))
         return false;
     TypeScript::SetThis(cx, script, efg.fp()->thisValue());
 
     Probes::startExecution(script);
     bool ok = RunScript(cx, script, efg.fp());
     Probes::stopExecution(script);
 
     /* Propgate the return value out. */
@@ -1169,16 +1169,17 @@ js::Interpret(JSContext *cx, StackFrame 
     RootedValue rootValue0(cx), rootValue1(cx);
     RootedString rootString0(cx), rootString1(cx);
     RootedObject rootObject0(cx), rootObject1(cx), rootObject2(cx);
     RootedFunction rootFunction0(cx);
     RootedTypeObject rootType0(cx);
     RootedPropertyName rootName0(cx);
     RootedId rootId0(cx);
     RootedShape rootShape0(cx);
+    RootedScript rootScript0(cx);
     DebugOnly<uint32_t> blockDepth;
 
     if (!entryFrame)
         entryFrame = regs.fp();
 
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
         JS_ASSERT(size_t(regs.pc - script->code) <= script->length);
@@ -2355,17 +2356,17 @@ 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);
-    RawScript funScript = fun->getOrCreateScript(cx);
+    RootedScript funScript(cx, fun->getOrCreateScript(cx));
     if (!funScript)
         goto error;
     if (!cx->stack.pushInlineFrame(cx, regs, args, *fun, funScript, initial))
         goto error;
 
     SET_SCRIPT(regs.fp()->script());
 #ifdef JS_METHODJIT
     script->resetLoopCount();
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2411,17 +2411,17 @@ js_CreateThisForFunction(JSContext *cx, 
 }
 
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
  * checking whether document.all is defined.
  */
 static bool
-Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
+Detecting(JSContext *cx, UnrootedScript script, jsbytecode *pc)
 {
     /* General case: a branch or equality op follows the access. */
     JSOp op = JSOp(*pc);
     if (js_CodeSpec[op].format & JOF_DETECTING)
         return true;
 
     jsbytecode *endpc = script->code + script->length;
     JS_ASSERT(script->code <= pc && pc < endpc);
@@ -2462,17 +2462,17 @@ Detecting(JSContext *cx, JSScript *scrip
 unsigned
 js_InferFlags(JSContext *cx, unsigned defaultFlags)
 {
     /*
      * We intentionally want to look across compartment boundaries to correctly
      * handle the case of cross-compartment property access.
      */
     jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc, ContextStack::ALLOW_CROSS_COMPARTMENT);
+    UnrootedScript script = cx->stack.currentScript(&pc, ContextStack::ALLOW_CROSS_COMPARTMENT);
     if (!script)
         return defaultFlags;
 
     const JSCodeSpec *cs = &js_CodeSpec[*pc];
     uint32_t format = cs->format;
     unsigned flags = 0;
     if (JOF_MODE(format) != JOF_NAME)
         flags |= JSRESOLVE_QUALIFIED;
@@ -4064,16 +4064,18 @@ CallResolveOp(JSContext *cx, HandleObjec
 
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 LookupPropertyWithFlagsInline(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                               MutableHandleObject objp, MutableHandleShape propp)
 {
+    AssertCanGC();
+
     /* Search scopes starting with obj and following the prototype link. */
     RootedObject current(cx, obj);
     while (true) {
         {
             UnrootedShape shape = current->nativeLookup(cx, id);
             if (shape) {
                 objp.set(current);
                 propp.set(shape);
@@ -4196,22 +4198,24 @@ js_NativeGetInline(JSContext *cx, Handle
         JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(),
                      js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
     } else {
         vp.setUndefined();
     }
     if (shape->hasDefaultGetter())
         return true;
 
-    jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc);
-    if (script && script->hasAnalysis()) {
-        analyze::Bytecode *code = script->analysis()->maybeCode(pc);
-        if (code)
-            code->accessGetter = true;
+    {
+        jsbytecode *pc;
+        UnrootedScript script = cx->stack.currentScript(&pc);
+        if (script && script->hasAnalysis()) {
+            analyze::Bytecode *code = script->analysis()->maybeCode(pc);
+            if (code)
+                code->accessGetter = true;
+        }
     }
 
     if (!shape->get(cx, receiver, obj, pobj, vp))
         return false;
 
     /* Update slotful shapes according to the value produced by the getter. */
     if (shape->hasSlot() && pobj->nativeContains(cx, shape))
         pobj->nativeSetSlot(shape->slot(), vp);
@@ -4311,17 +4315,17 @@ js_GetPropertyHelperInline(JSContext *cx
                 return false;
             }
 
             /* Don't warn if not strict or for random getprop operations. */
             if (!cx->hasStrictOption() || (op != JSOP_GETPROP && op != JSOP_GETELEM))
                 return true;
 
             /* Don't warn repeatedly for the same script. */
-            JSScript *script = cx->stack.currentScript();
+            RootedScript script(cx, cx->stack.currentScript());
             if (!script || script->warnedAboutUndefinedProp)
                 return true;
 
             /*
              * XXX do not warn about missing __iterator__ as the function
              * may be called from JS_GetMethodById. See bug 355145.
              */
             if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
@@ -4426,23 +4430,25 @@ js::GetMethod(JSContext *cx, HandleObjec
         return js_GetXMLMethod(cx, obj, id, vp);
 #endif
     return op(cx, obj, obj, id, vp);
 }
 
 JS_FRIEND_API(bool)
 js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
 {
-    JSScript *script = cx->stack.currentScript(NULL, ContextStack::ALLOW_CROSS_COMPARTMENT);
-    if (!script)
-        return true;
-
-    /* If neither cx nor the code is strict, then no check is needed. */
-    if (!script->strict && !cx->hasStrictOption())
-        return true;
+    {
+        UnrootedScript script = cx->stack.currentScript(NULL, ContextStack::ALLOW_CROSS_COMPARTMENT);
+        if (!script)
+            return true;
+
+        /* If neither cx nor the code is strict, then no check is needed. */
+        if (!script->strict && !cx->hasStrictOption())
+            return true;
+    }
 
     JSAutoByteString bytes(cx, propname);
     return !!bytes &&
            JS_ReportErrorFlagsAndNumber(cx,
                                         (JSREPORT_WARNING | JSREPORT_STRICT
                                          | JSREPORT_STRICT_MODE_ERROR),
                                         js_GetErrorMessage, NULL,
                                         JSMSG_UNDECLARED_VAR, bytes.ptr());
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -68,17 +68,17 @@ void
 Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void* address)
 {
     if (fop->runtime()->spsProfiler.enabled())
         fop->runtime()->spsProfiler.discardMJITCode(jscr, chunk, address);
 }
 
 bool
 Probes::registerICCode(JSContext *cx,
-                       mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
+                       mjit::JITChunk *chunk, UnrootedScript script, jsbytecode* pc,
                        void *start, size_t size)
 {
     if (cx->runtime->spsProfiler.enabled() &&
         !cx->runtime->spsProfiler.registerICCode(chunk, script, pc, start, size))
     {
         return false;
     }
     return true;
@@ -144,17 +144,17 @@ Probes::shutdown()
         ok = false;
 #endif
 
     return ok;
 }
 
 #ifdef INCLUDE_MOZILLA_DTRACE
 static const char *
-ScriptFilename(const JSScript *script)
+ScriptFilename(const UnrootedScript script)
 {
     if (!script)
         return Probes::nullName;
     if (!script->filename)
         return Probes::anonymousName;
     return script->filename;
 }
 
@@ -171,37 +171,37 @@ FunctionName(JSContext *cx, const JSFunc
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
  * to reduce any negative compiler optimization effect that the addition of
  * a number of usually unused lines of code would cause.
  */
 void
-Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script)
 {
     JSAutoByteString funNameBytes;
     JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), Probes::nullName,
                               FunctionName(cx, fun, &funNameBytes));
 }
 
 void
-Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
+Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script)
 {
     JSAutoByteString funNameBytes;
     JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), Probes::nullName,
                                FunctionName(cx, fun, &funNameBytes));
 }
 #endif
 
 #ifdef MOZ_ETW
 static void
 current_location(JSContext *cx, int* lineno, char const **filename)
 {
-    JSScript *script = cx->stack.currentScript()
+    UnrootedScript script = cx->stack.currentScript()
     if (! script) {
         *lineno = -1;
         *filename = "(uninitialized)";
         return;
     }
     *lineno = js_PCToLineNumber(cx, script, js_GetCurrentBytecodePC(cx));
     *filename = ScriptFilename(script);
 }
@@ -238,27 +238,27 @@ Probes::ETWDestroyRuntime(JSRuntime *rt)
 bool
 Probes::ETWShutdown()
 {
     EventUnregisterMozillaSpiderMonkey();
     return true;
 }
 
 bool
-Probes::ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
+Probes::ETWEnterJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script, int counter)
 {
     int lineno = script ? script->lineno : -1;
     JSAutoByteString bytes;
     return (EventWriteEvtFunctionEntry(ScriptFilename(script), lineno,
                                        ObjectClassname((JSObject *)fun),
                                        FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
 }
 
 bool
-Probes::ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
+Probes::ETWExitJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script, int counter)
 {
     int lineno = script ? script->lineno : -1;
     JSAutoByteString bytes;
     return (EventWriteEvtFunctionExit(ScriptFilename(script), lineno,
                                       ObjectClassname((JSObject *)fun),
                                       FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
 }
 
@@ -420,24 +420,24 @@ Probes::ETWCustomMark(const char *string
 
 bool
 Probes::ETWCustomMark(int marker)
 {
     return EventWriteEvtCustomInt(marker) == ERROR_SUCCESS;
 }
 
 bool
-Probes::ETWStartExecution(JSScript *script)
+Probes::ETWStartExecution(UnrootedScript script)
 {
     int lineno = script ? script->lineno : -1;
     return EventWriteEvtExecuteStart(ScriptFilename(script), lineno) == ERROR_SUCCESS;
 }
 
 bool
-Probes::ETWStopExecution(JSScript *script)
+Probes::ETWStopExecution(UnrootedScript script)
 {
     int lineno = script ? script->lineno : -1;
     return EventWriteEvtExecuteDone(ScriptFilename(script), lineno) == ERROR_SUCCESS;
 }
 
 bool
 Probes::ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize)
 {
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -87,26 +87,26 @@ bool callTrackingActive(JSContext *);
 
 /*
  * Test whether anything is looking for JIT native code registration events.
  * This information will not be collected otherwise.
  */
 bool wantNativeAddressInfo(JSContext *);
 
 /* Entering a JS function */
-bool enterScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
+bool enterScript(JSContext *, UnrootedScript , JSFunction *, StackFrame *);
 
 /* About to leave a JS function */
-bool exitScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
+bool exitScript(JSContext *, UnrootedScript , JSFunction *, StackFrame *);
 
 /* Executing a script */
-bool startExecution(JSScript *script);
+bool startExecution(UnrootedScript script);
 
 /* Script has completed execution */
-bool stopExecution(JSScript *script);
+bool stopExecution(UnrootedScript script);
 
 /* Heap has been resized */
 bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
 
 /*
  * Object has been created. |obj| must exist (its class and size are read)
  */
 bool createObject(JSContext *cx, JSObject *obj);
@@ -218,47 +218,47 @@ registerMJITCode(JSContext *cx, js::mjit
 void
 discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void* address);
 
 /*
  * IC code has been allocated within the given JITChunk
  */
 bool
 registerICCode(JSContext *cx,
-               mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
+               mjit::JITChunk *chunk, UnrootedScript script, jsbytecode* pc,
                void *start, size_t size);
 #endif /* JS_METHODJIT */
 
 /*
  * A whole region of code has been deallocated, containing any number of ICs.
  * (ICs are unregistered in a batch, so individual ICs are not registered.)
  */
 void
 discardExecutableRegion(void *start, size_t size);
 
 /*
  * Internal: DTrace-specific functions to be called during Probes::enterScript
  * and Probes::exitScript. These will not be inlined, but the argument
  * marshalling required for these probe points is expensive enough that it
  * shouldn't really matter.
  */
-void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
-void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script);
+void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script);
+void DTraceExitJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script);
 
 /*
  * Internal: ETW-specific probe functions
  */
 #ifdef MOZ_ETW
 // ETW Handlers
 bool ETWCreateRuntime(JSRuntime *rt);
 bool ETWDestroyRuntime(JSRuntime *rt);
 bool ETWShutdown();
 bool ETWCallTrackingActive();
-bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
-bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter);
+bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script, int counter);
+bool ETWExitJSFun(JSContext *cx, JSFunction *fun, UnrootedScript script, int counter);
 bool ETWCreateObject(JSContext *cx, JSObject *obj);
 bool ETWFinalizeObject(JSObject *obj);
 bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize);
 bool ETWCreateString(JSContext *cx, JSString *string, size_t length);
 bool ETWFinalizeString(JSString *string);
 bool ETWCompileScriptBegin(const char *filename, int lineno);
 bool ETWCompileScriptEnd(const char *filename, int lineno);
 bool ETWCalloutBegin(JSContext *cx, JSFunction *fun);
@@ -269,18 +269,18 @@ bool ETWGCStart();
 bool ETWGCEnd();
 bool ETWGCStartMarkPhase();
 bool ETWGCEndMarkPhase();
 bool ETWGCStartSweepPhase();
 bool ETWGCEndSweepPhase();
 bool ETWCustomMark(JSString *string);
 bool ETWCustomMark(const char *string);
 bool ETWCustomMark(int marker);
-bool ETWStartExecution(JSScript *script);
-bool ETWStopExecution(JSContext *cx, JSScript *script);
+bool ETWStartExecution(UnrootedScript script);
+bool ETWStopExecution(JSContext *cx, UnrootedScript script);
 bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize);
 #endif
 
 } /* namespace Probes */
 
 /*
  * Many probe handlers are implemented inline for minimal performance impact,
  * especially important when no backends are enabled.
@@ -307,17 +307,17 @@ Probes::callTrackingActive(JSContext *cx
 inline bool
 Probes::wantNativeAddressInfo(JSContext *cx)
 {
     return (cx->reportGranularity >= JITREPORT_GRANULARITY_FUNCTION &&
             JITGranularityRequested(cx) >= JITREPORT_GRANULARITY_FUNCTION);
 }
 
 inline bool
-Probes::enterScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+Probes::enterScript(JSContext *cx, UnrootedScript script, JSFunction *maybeFun,
                     StackFrame *fp)
 {
     bool ok = true;
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
         DTraceEnterJSFun(cx, maybeFun, script);
 #endif
 #ifdef MOZ_TRACE_JSCALLS
@@ -334,17 +334,17 @@ Probes::enterScript(JSContext *cx, JSScr
         JS_ASSERT_IF(!fp->isGeneratorFrame(), !fp->hasPushedSPSFrame());
         fp->setPushedSPSFrame();
     }
 
     return ok;
 }
 
 inline bool
-Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+Probes::exitScript(JSContext *cx, UnrootedScript script, JSFunction *maybeFun,
                    StackFrame *fp)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
         DTraceExitJSFun(cx, maybeFun, script);
 #endif
@@ -676,17 +676,17 @@ Probes::CustomMark(int marker)
     if (ProfilingActive && !ETWCustomMark(marker))
         ok = false;
 #endif
 
     return ok;
 }
 
 inline bool
-Probes::startExecution(JSScript *script)
+Probes::startExecution(UnrootedScript script)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_EXECUTE_START_ENABLED())
         JAVASCRIPT_EXECUTE_START((script->filename ? (char *)script->filename : nullName),
                                  script->lineno);
 #endif
@@ -694,17 +694,17 @@ Probes::startExecution(JSScript *script)
     if (ProfilingActive && !ETWStartExecution(script))
         ok = false;
 #endif
 
     return ok;
 }
 
 inline bool
-Probes::stopExecution(JSScript *script)
+Probes::stopExecution(UnrootedScript script)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
         JAVASCRIPT_EXECUTE_DONE((script->filename ? (char *)script->filename : nullName),
                                 script->lineno);
 #endif
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -103,17 +103,17 @@ PropertyCache::fill(JSContext *cx, JSObj
     return entry;
 }
 
 PropertyName *
 PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
                         PropertyCacheEntry *entry)
 {
     JSObject *obj, *pobj;
-    JSScript *script = cx->stack.currentScript();
+    RootedScript script(cx, cx->stack.currentScript());
 
     JS_ASSERT(this == &cx->propertyCache());
     JS_ASSERT(uint32_t(pc - script->code) < script->length);
 
     JSOp op = JSOp(*pc);
 
     obj = *objp;
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -273,26 +273,26 @@ typedef JSTrapStatus
 typedef JSBool
 (* JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsid id, jsval old,
                         jsval *newp, void *closure);
 
 /* called just after script creation */
 typedef void
 (* JSNewScriptHook)(JSContext  *cx,
                     const char *filename,  /* URL of script */
-                    unsigned      lineno,     /* first line */
+                    unsigned   lineno,     /* first line */
                     JSScript   *script,
                     JSFunction *fun,
                     void       *callerdata);
 
 /* called just before script destruction */
 typedef void
 (* JSDestroyScriptHook)(JSFreeOp *fop,
-                        JSRawScript script,
-                        void      *callerdata);
+                        JSScript *script,
+                        void     *callerdata);
 
 typedef void
 (* JSSourceHandler)(const char *filename, unsigned lineno, const jschar *str,
                     size_t length, void **listenerTSData, void *closure);
 
 /*
  * This hook captures high level script execution and function calls (JS or
  * native).  It is used by JS_SetExecuteHook to hook top level scripts and by
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -841,17 +841,17 @@ JSScript::initScriptCounts(JSContext *cx
     /* Enable interrupts in any interpreter frames running on this script. */
     InterpreterFrames *frames;
     for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
 
     return true;
 }
 
-static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript *script)
+static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(UnrootedScript script)
 {
     JS_ASSERT(script->hasScriptCounts);
     ScriptCountsMap *map = script->compartment()->scriptCountsMap;
     ScriptCountsMap::Ptr p = map->lookup(script);
     JS_ASSERT(p);
     return p;
 }
 
@@ -1566,24 +1566,24 @@ ScriptDataSize(uint32_t length, uint32_t
         size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
 
     size += nbindings * sizeof(Binding);
     size += length * sizeof(jsbytecode);
     size += nsrcnotes * sizeof(jssrcnote);
     return size;
 }
 
-JSScript *
+UnrootedScript
 JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun,
                  const CompileOptions &options, unsigned staticLevel,
                  ScriptSource *ss, uint32_t bufStart, uint32_t bufEnd)
 {
     RootedScript script(cx, js_NewGCScript(cx));
     if (!script)
-        return NULL;
+        return UnrootedScript(NULL);
 
     PodZero(script.get());
     new (&script->bindings) Bindings;
 
     script->enclosingScope_ = enclosingScope;
     script->savedCallerFun = savedCallerFun;
 
     /* Establish invariant: principals implies originPrincipals. */
@@ -1605,17 +1605,17 @@ JSScript::Create(JSContext *cx, HandleOb
     JS_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
 
     // This is an unsigned-to-uint16_t conversion, test for too-high values.
     // In practice, recursion in Parser and/or BytecodeEmitter will blow the
     // stack if we nest functions more than a few hundred deep, so this will
     // never trigger.  Oh well.
     if (staticLevel > UINT16_MAX) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_DEEP, js_function_str);
-        return NULL;
+        return UnrootedScript(NULL);
     }
     script->staticLevel = uint16_t(staticLevel);
 
     script->setScriptSource(ss);
     script->sourceStart = bufStart;
     script->sourceEnd = bufEnd;
     script->userBit = options.userBit;
 
@@ -1880,30 +1880,32 @@ JSScript::enclosingScriptsCompiledSucces
             enclosing = fun->nonLazyScript()->enclosingScope_;
         } else {
             enclosing = enclosing->asStaticBlock().enclosingStaticScope();
         }
     }
     return true;
 }
 
-JS_FRIEND_API(void)
-js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
+void
+js::CallNewScriptHook(JSContext *cx, HandleScript script, HandleFunction fun)
 {
+    AssertCanGC();
     JS_ASSERT(!script->isActiveEval);
     if (JSNewScriptHook hook = cx->runtime->debugHooks.newScriptHook) {
         AutoKeepAtoms keep(cx->runtime);
         hook(cx, script->filename, script->lineno, script, fun,
              cx->runtime->debugHooks.newScriptHookData);
     }
 }
 
 void
 js::CallDestroyScriptHook(FreeOp *fop, RawScript script)
 {
+    // The hook will only call into JS if a GC is not running.
     if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook)
         hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData);
     script->clearTraps(fop);
 }
 
 void
 JSScript::finalize(FreeOp *fop)
 {
@@ -2162,17 +2164,17 @@ js::CurrentScriptFileLineOriginSlow(JSCo
 template <class T>
 static inline T *
 Rebase(RawScript dst, RawScript src, T *srcp)
 {
     size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
     return reinterpret_cast<T *>(dst->data + off);
 }
 
-JSScript *
+UnrootedScript
 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;
@@ -2181,25 +2183,25 @@ js::CloneScript(JSContext *cx, HandleObj
 
     /* Script data */
 
     size_t size = ScriptDataSize(src->length, src->numNotes(), src->bindings.count(), src->natoms,
                                  nobjects, nregexps, ntrynotes, nconsts);
 
     uint8_t *data = AllocScriptData(cx, size);
     if (!data)
-        return NULL;
+        return UnrootedScript(NULL);
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
     InternalHandle<Bindings*> bindingsHandle =
         InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
-        return NULL;
+        return UnrootedScript(NULL);
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject *vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
             RootedObject obj(cx, vector[i]);
@@ -2230,29 +2232,29 @@ js::CloneScript(JSContext *cx, HandleObj
                  * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that
                  * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled
                  * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this
                  * case should only ever be hit when cloning objects from self-hosted code.
                  */
                 clone = CloneObjectLiteral(cx, cx->global(), obj);
             }
             if (!clone || !objects.append(clone))
-                return NULL;
+                return UnrootedScript(NULL);
         }
     }
 
     /* RegExps */
 
     AutoObjectVector regexps(cx);
     for (unsigned i = 0; i < nregexps; i++) {
         HeapPtrObject *vector = src->regexps()->vector;
         for (unsigned i = 0; i < nregexps; i++) {
             RawObject clone = CloneScriptRegExpObject(cx, vector[i]->asRegExp());
             if (!clone || !regexps.append(clone))
-                return NULL;
+                return UnrootedScript(NULL);
         }
     }
 
     /* Now that all fallible allocation is complete, create the GC thing. */
 
     CompileOptions options(cx);
     options.setPrincipals(cx->compartment->principals)
            .setOriginPrincipals(src->originPrincipals)
@@ -2260,17 +2262,17 @@ js::CloneScript(JSContext *cx, HandleObj
            .setNoScriptRval(src->noScriptRval)
            .setVersion(src->getVersion())
            .setUserBit(src->userBit);
     RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun,
                                           options, src->staticLevel,
                                           src->scriptSource(), src->sourceStart, src->sourceEnd));
     if (!dst) {
         js_free(data);
-        return NULL;
+        return UnrootedScript(NULL);
     }
     AutoAssertNoGC nogc;
 
     dst->bindings = bindings;
 
     /* This assignment must occur before all the Rebase calls. */
     dst->data = data;
     memcpy(data, src->data, size);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -235,19 +235,19 @@ class ScriptCounts
     inline void destroy(FreeOp *fop);
 
     void set(js::ScriptCounts counts) {
         pcCountsVector = counts.pcCountsVector;
         ionCounts = counts.ionCounts;
     }
 };
 
-typedef HashMap<JSScript *,
+typedef HashMap<RawScript,
                 ScriptCounts,
-                DefaultHasher<JSScript *>,
+                DefaultHasher<RawScript>,
                 SystemAllocPolicy> ScriptCountsMap;
 
 class DebugScript
 {
     friend struct ::JSScript;
 
     /*
      * When non-zero, compile script in single-step mode. The top bit is set and
@@ -263,19 +263,19 @@ class DebugScript
 
     /*
      * Array with all breakpoints installed at opcodes in the script, indexed
      * by the offset of the opcode into the script.
      */
     BreakpointSite  *breakpoints[1];
 };
 
-typedef HashMap<JSScript *,
+typedef HashMap<RawScript,
                 DebugScript *,
-                DefaultHasher<JSScript *>,
+                DefaultHasher<RawScript>,
                 SystemAllocPolicy> DebugScriptMap;
 
 struct ScriptSource;
 
 } /* namespace js */
 
 struct JSScript : public js::gc::Cell
 {
@@ -507,19 +507,19 @@ struct JSScript : public js::gc::Cell
     bool            needsArgsAnalysis_:1;
     bool            needsArgsObj_:1;
 
     //
     // End of fields.  Start methods.
     //
 
   public:
-    static JSScript *Create(JSContext *cx, js::HandleObject enclosingScope, bool savedCallerFun,
-                            const JS::CompileOptions &options, unsigned staticLevel,
-                            js::ScriptSource *ss, uint32_t sourceStart, uint32_t sourceEnd);
+    static js::UnrootedScript Create(JSContext *cx, js::HandleObject enclosingScope, bool savedCallerFun,
+                                    const JS::CompileOptions &options, unsigned staticLevel,
+                                    js::ScriptSource *ss, uint32_t sourceStart, uint32_t sourceEnd);
 
     // Three ways ways to initialize a JSScript.  Callers of partiallyInit()
     // and fullyInitTrivial() are responsible for notifying the debugger after
     // successfully creating any kind (function or other) of new JSScript.
     // However, callers of fullyInitFromEmitter() do not need to do this.
     static bool partiallyInit(JSContext *cx, JS::Handle<JSScript*> script,
                               uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
                               uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
@@ -637,20 +637,20 @@ struct JSScript : public js::gc::Cell
 
     /* Ensure the script has a TypeScript. */
     inline bool ensureHasTypes(JSContext *cx);
 
     /*
      * Ensure the script has bytecode analysis information. Performed when the
      * script first runs, or first runs after a TypeScript GC purge.
      */
-    inline bool ensureRanAnalysis(JSContext *cx);
+    static inline bool ensureRanAnalysis(JSContext *cx, JS::HandleScript script);
 
     /* Ensure the script has type inference analysis information. */
-    inline bool ensureRanInference(JSContext *cx);
+    static inline bool ensureRanInference(JSContext *cx, JS::HandleScript script);
 
     inline bool hasAnalysis();
     inline void clearAnalysis();
     inline js::analyze::ScriptAnalysis *analysis();
 
     /* Heuristic to check if the function is expected to be "short running". */
     bool isShortRunning();
 
@@ -767,17 +767,17 @@ struct JSScript : public js::gc::Cell
 
     uint32_t numNotes();  /* Number of srcnote slots in the srcnotes section */
 
     /* Script notes are allocated right after the code. */
     jssrcnote *notes() { return (jssrcnote *)(code + length); }
 
     bool hasArray(ArrayKind kind)           { return (hasArrayBits & (1 << kind)); }
     void setHasArray(ArrayKind kind)        { hasArrayBits |= (1 << kind); }
-    void cloneHasArray(JSScript *script)    { hasArrayBits = script->hasArrayBits; }
+    void cloneHasArray(js::UnrootedScript script) { hasArrayBits = script->hasArrayBits; }
 
     bool hasConsts()        { return hasArray(CONSTS);      }
     bool hasObjects()       { return hasArray(OBJECTS);     }
     bool hasRegexps()       { return hasArray(REGEXPS);     }
     bool hasTrynotes()      { return hasArray(TRYNOTES);    }
 
     #define OFF(fooOff, hasFoo, t)   (fooOff() + (hasFoo() ? sizeof(t) : 0))
 
@@ -915,18 +915,18 @@ struct JSScript : public js::gc::Cell
     bool stepModeEnabled() { return hasDebugScript && !!debugScript()->stepMode; }
 
 #ifdef DEBUG
     uint32_t stepModeCount() { return hasDebugScript ? (debugScript()->stepMode & stepCountMask) : 0; }
 #endif
 
     void finalize(js::FreeOp *fop);
 
-    static inline void writeBarrierPre(JSScript *script);
-    static inline void writeBarrierPost(JSScript *script, void *addr);
+    static inline void writeBarrierPre(js::UnrootedScript script);
+    static inline void writeBarrierPost(js::UnrootedScript script, void *addr);
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
 
     static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
                                                    JSPrincipals *originPrincipals) {
         return originPrincipals ? originPrincipals : principals;
     }
 
@@ -992,42 +992,28 @@ class AliasedFormalIter
     unsigned slot_;
 
     void settle() {
         while (p_ != end_ && !p_->aliased())
             p_++;
     }
 
   public:
-    explicit inline AliasedFormalIter(JSScript *script);
+    explicit inline AliasedFormalIter(js::UnrootedScript script);
 
     bool done() const { return p_ == end_; }
     operator bool() const { return !done(); }
     void operator++(int) { JS_ASSERT(!done()); p_++; slot_++; settle(); }
 
     const Binding &operator*() const { JS_ASSERT(!done()); return *p_; }
     const Binding *operator->() const { JS_ASSERT(!done()); return p_; }
     unsigned frameIndex() const { JS_ASSERT(!done()); return p_ - begin_; }
     unsigned scopeSlot() const { JS_ASSERT(!done()); return slot_; }
 };
 
-}  /* namespace js */
-
-/*
- * New-script-hook calling is factored from JSScript::fullyInitFromEmitter() so
- * that it and callers of XDRScript() can share this code.  In the case of
- * callers of XDRScript(), the hook should be invoked only after successful
- * decode of any owning function (the fun parameter) or script object (null
- * fun).
- */
-extern JS_FRIEND_API(void)
-js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
-
-namespace js {
-
 struct SourceCompressionToken;
 
 struct ScriptSource
 {
     friend class SourceCompressorThread;
   private:
     union {
         // Before setSourceCopy or setSource are successfully called, this union
@@ -1208,16 +1194,26 @@ struct SourceCompressionToken
         complete();
     }
 
     bool complete();
     void abort();
     bool active() const { return !!ss; }
 };
 
+/*
+ * New-script-hook calling is factored from JSScript::fullyInitFromEmitter() so
+ * that it and callers of XDRScript() can share this code.  In the case of
+ * callers of XDRScript(), the hook should be invoked only after successful
+ * decode of any owning function (the fun parameter) or script object (null
+ * fun).
+ */
+extern void
+CallNewScriptHook(JSContext *cx, JS::HandleScript script, JS::HandleFunction fun);
+
 extern void
 CallDestroyScriptHook(FreeOp *fop, js::RawScript script);
 
 extern const char *
 SaveScriptFilename(JSContext *cx, const char *filename);
 
 struct ScriptFilenameEntry
 {
@@ -1248,17 +1244,18 @@ MarkScriptFilename(JSRuntime *rt, const 
 extern void
 SweepScriptFilenames(JSRuntime *rt);
 
 extern void
 FreeScriptFilenames(JSRuntime *rt);
 
 struct ScriptAndCounts
 {
-    JSScript *script;
+    /* This structure is stored and marked from the JSRuntime. */
+    js::RawScript script;
     ScriptCounts scriptCounts;
 
     PCCounts &getPCCounts(jsbytecode *pc) const {
         JS_ASSERT(unsigned(pc - script->code) < script->length);
         return scriptCounts.pcCountsVector[pc - script->code];
     }
 
     ion::IonScriptCounts *getIonCounts() const {
@@ -1301,17 +1298,17 @@ CurrentLine(JSContext *cx);
 enum LineOption {
     CALLED_FROM_JSOP_EVAL,
     NOT_CALLED_FROM_JSOP_EVAL
 };
 
 inline void
 CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL);
 
-extern JSScript *
+extern UnrootedScript
 CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script);
 
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * js_CallNewScriptHook.
  */
 template<XDRMode mode>
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -23,17 +23,17 @@
 namespace js {
 
 inline
 Bindings::Bindings()
     : callObjShape_(NULL), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT), numArgs_(0), numVars_(0)
 {}
 
 inline
-AliasedFormalIter::AliasedFormalIter(JSScript *script)
+AliasedFormalIter::AliasedFormalIter(js::UnrootedScript script)
   : begin_(script->bindings.bindingArray()),
     p_(begin_),
     end_(begin_ + (script->funHasAnyAliasedFormal ? script->bindings.numArgs() : 0)),
     slot_(CallObject::RESERVED_SLOTS)
 {
     settle();
 }
 
@@ -146,30 +146,30 @@ inline void
 JSScript::destroyMJITInfo(js::FreeOp *fop)
 {
     fop->delete_(mJITInfo);
     mJITInfo = NULL;
 }
 #endif /* JS_METHODJIT */
 
 inline void
-JSScript::writeBarrierPre(JSScript *script)
+JSScript::writeBarrierPre(js::UnrootedScript script)
 {
 #ifdef JSGC_INCREMENTAL
     if (!script)
         return;
 
     JSCompartment *comp = script->compartment();
     if (comp->needsBarrier()) {
         JS_ASSERT(!comp->rt->isHeapBusy());
-        JSScript *tmp = script;
+        js::UnrootedScript tmp = script;
         MarkScriptUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
         JS_ASSERT(tmp == script);
     }
 #endif
 }
 
 inline void
-JSScript::writeBarrierPost(JSScript *script, void *addr)
+JSScript::writeBarrierPost(js::UnrootedScript script, void *addr)
 {
 }
 
 #endif /* jsscriptinlines_h___ */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -156,25 +156,25 @@ mjit::Compiler::compile()
     }
 
     return status;
 }
 
 CompileStatus
 mjit::Compiler::checkAnalysis(HandleScript script)
 {
-    if (!script->ensureRanAnalysis(cx))
+    if (!JSScript::ensureRanAnalysis(cx, script))
         return Compile_Error;
 
     if (!script->analysis()->jaegerCompileable()) {
         JaegerSpew(JSpew_Abort, "script has uncompileable opcodes\n");
         return Compile_Abort;
     }
 
-    if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx))
+    if (cx->typeInferenceEnabled() && !JSScript::ensureRanInference(cx, script))
         return Compile_Error;
 
     ScriptAnalysis *analysis = script->analysis();
     analysis->assertMatchingDebugMode();
     if (analysis->failed()) {
         JaegerSpew(JSpew_Abort, "couldn't analyze bytecode; probably switchX or OOM\n");
         return Compile_Abort;
     }
@@ -667,34 +667,35 @@ static uint32_t CHUNK_LIMIT = 1500;
 void
 mjit::SetChunkLimit(uint32_t limit)
 {
     if (limit)
         CHUNK_LIMIT = limit;
 }
 
 JITScript *
-MakeJITScript(JSContext *cx, JSScript *script)
-{
-    if (!script->ensureRanAnalysis(cx))
+MakeJITScript(JSContext *cx, HandleScript script)
+{
+    AssertCanGC();
+    if (!JSScript::ensureRanAnalysis(cx, script))
         return NULL;
 
     ScriptAnalysis *analysis = script->analysis();
 
     Vector<ChunkDescriptor> chunks(cx);
     Vector<CrossChunkEdge> edges(cx);
 
     if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) {
         ChunkDescriptor desc;
         desc.begin = 0;
         desc.end = script->length;
         if (!chunks.append(desc))
             return NULL;
     } else {
-        if (!script->ensureRanInference(cx))
+        if (!JSScript::ensureRanInference(cx, script))
             return NULL;
 
         /* Outgoing edges within the current chunk. */
         Vector<CrossChunkEdge> currentEdges(cx);
         uint32_t chunkStart = 0;
 
         bool preserveNextChunk = false;
         unsigned offset, nextOffset = 0;
@@ -988,21 +989,20 @@ IonGetsFirstChance(JSContext *cx, JSScri
         return false;
 
     return true;
 #endif
     return false;
 }
 
 CompileStatus
-mjit::CanMethodJIT(JSContext *cx, JSScript *scriptArg, jsbytecode *pc,
+mjit::CanMethodJIT(JSContext *cx, HandleScript script, jsbytecode *pc,
                    bool construct, CompileRequest request, StackFrame *frame)
 {
     bool compiledOnce = false;
-    RootedScript script(cx, scriptArg);
   checkOutput:
     if (!cx->methodJitEnabled)
         return Compile_Abort;
 
     /*
      * If SPS (profiling) is enabled, then the emitted instrumentation has to be
      * careful to not wildly write to random locations. This is relevant
      * whenever the status of profiling (on/off) is changed while JS is running.
@@ -1015,17 +1015,17 @@ mjit::CanMethodJIT(JSContext *cx, JSScri
      * SPS frame has not been pushed, and SPS is not enabled, then we're still
      * good to go. If, however, the two are different, then we cannot emit JIT
      * code because the instrumentation will be wrong one way or another.
      */
     if (frame->script() == script && pc != script->code) {
         if (frame->hasPushedSPSFrame() != cx->runtime->spsProfiler.enabled())
             return Compile_Skipped;
     }
-		
+
     if (IonGetsFirstChance(cx, script, request))
         return Compile_Skipped;
 
     if (script->hasMJITInfo()) {
         JSScript::JITScriptHandle *jith = script->jitHandle(construct, cx->compartment->compileBarriers());
         if (jith->isUnjittable())
             return Compile_Abort;
     }
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -608,17 +608,17 @@ js_InternalThrow(VMFrame &f)
      * 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.
      */
     cx->jaegerRuntime().setLastUnfinished(Jaeger_Unfinished);
 
-    if (!script->ensureRanAnalysis(cx)) {
+    if (!JSScript::ensureRanAnalysis(cx, script)) {
         js_ReportOutOfMemory(cx);
         return NULL;
     }
 
     analyze::AutoEnterAnalysis enter(cx);
 
     /*
      * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
@@ -703,21 +703,21 @@ stubs::ScriptProbeOnlyEpilogue(VMFrame &
 void JS_FASTCALL
 stubs::CrossChunkShim(VMFrame &f, void *edge_)
 {
     AssertCanGC();
     DebugOnly<CrossChunkEdge*> edge = (CrossChunkEdge *) edge_;
 
     mjit::ExpandInlineFrames(f.cx->compartment);
 
-    UnrootedScript script = f.script();
+    RootedScript script(f.cx, f.script());
     JS_ASSERT(edge->target < script->length);
     JS_ASSERT(script->code + edge->target == f.pc());
 
-    CompileStatus status = CanMethodJIT(f.cx, DropUnrooted(script), f.pc(),
+    CompileStatus status = CanMethodJIT(f.cx, script, f.pc(),
                                         f.fp()->isConstructing(),
                                         CompileRequest_Interpreter, f.fp());
     if (status == Compile_Error)
         THROW();
 
     void **addr = f.returnAddressLocation();
     *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
 
@@ -753,17 +753,17 @@ js_InternalInterpret(void *returnData, v
     JSContext *cx = f.cx;
     StackFrame *fp = f.regs.fp();
     RootedScript script(cx, fp->script());
 
     jsbytecode *pc = f.regs.pc;
 
     JSOp op = JSOp(*pc);
 
-    if (!script->ensureRanAnalysis(cx)) {
+    if (!JSScript::ensureRanAnalysis(cx, script)) {
         js_ReportOutOfMemory(cx);
         return js_InternalThrow(f);
     }
 
     analyze::AutoEnterAnalysis enter(cx);
     analyze::ScriptAnalysis *analysis = script->analysis();
 
     /*
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -909,17 +909,17 @@ ProfileStubCall(VMFrame &f);
 
 enum CompileRequest
 {
     CompileRequest_Interpreter,
     CompileRequest_JIT
 };
 
 CompileStatus
-CanMethodJIT(JSContext *cx, JSScript *script, jsbytecode *pc,
+CanMethodJIT(JSContext *cx, HandleScript script, jsbytecode *pc,
              bool construct, CompileRequest request, StackFrame *sp);
 
 inline void
 ReleaseScriptCode(FreeOp *fop, JSScript *script)
 {
     if (!script->hasMJITInfo())
         return;
 
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -414,19 +414,19 @@ class SetPropCompiler : public PICStubCo
 
         if (!type->unknownProperties()) {
             types::AutoEnterTypeInference enter(cx);
             types::TypeSet *types = type->getProperty(cx, types::MakeTypeId(cx, id), true);
             if (!types)
                 return false;
 
             jsbytecode *pc;
-            JSScript *script = cx->stack.currentScript(&pc);
-
-            if (!script->ensureRanInference(cx) || monitor.recompiled())
+            RootedScript script(cx, cx->stack.currentScript(&pc));
+
+            if (!JSScript::ensureRanInference(cx, script) || monitor.recompiled())
                 return false;
 
             JS_ASSERT(*pc == JSOP_SETPROP || *pc == JSOP_SETNAME);
 
             types::StackTypeSet *rhsTypes = script->analysis()->poppedTypes(pc, 0);
             rhsTypes->addSubset(cx, types);
         }
 
@@ -2214,17 +2214,17 @@ frameCountersOffset(VMFrame &f)
         offset += cx->fp()->script()->length;
         uint32_t index = cx->regs().inlined()->inlineIndex;
         InlineFrame *frames = f.chunk()->inlineFrames();
         for (unsigned i = 0; i < index; i++)
             offset += frames[i].fun->nonLazyScript()->length;
     }
 
     jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc);
+    UnrootedScript script = cx->stack.currentScript(&pc);
     offset += pc - script->code;
 
     return offset;
 }
 
 LookupStatus
 BaseIC::disable(VMFrame &f, const char *reason, void *stub)
 {
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -829,17 +829,18 @@ stubs::TriggerIonCompile(VMFrame &f)
          */
         if (!script->canIonCompile() || script->isIonCompilingOffThread())
             return;
 
         jsbytecode *osrPC = f.regs.pc;
         if (*osrPC != JSOP_LOOPENTRY)
             osrPC = NULL;
 
-        if (!ion::TestIonCompile(f.cx, script, script->function(), osrPC, f.fp()->isConstructing())) {
+        RootedFunction scriptFunction(f.cx, script->function());
+        if (!ion::TestIonCompile(f.cx, script, scriptFunction, osrPC, f.fp()->isConstructing())) {
             if (f.cx->isExceptionPending())
                 THROW();
         }
 
         return;
     }
 
     ExpandInlineFrames(f.cx->compartment);
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -515,19 +515,19 @@ SkipUTF8BOM(FILE* file)
     if (ch1 != EOF)
         ungetc(ch1, file);
 }
 
 static void
 Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
 {
     bool ok, hitEOF;
-    JSScript *script;
+    RootedScript script(cx);
     jsval result;
-    JSString *str;
+    RootedString str(cx);
     char *buffer;
     size_t size;
     jschar *uc_buffer;
     size_t uc_len;
     int lineno;
     int startline;
     FILE *file;
     uint32_t oldopts;
@@ -1050,17 +1050,17 @@ Evaluate(JSContext *cx, unsigned argc, j
         uint32_t saved = JS_GetOptions(cx);
         uint32_t options = saved & ~(JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         if (compileAndGo)
             options |= JSOPTION_COMPILE_N_GO;
         if (noScriptRval)
             options |= JSOPTION_NO_SCRIPT_RVAL;
 
         JS_SetOptions(cx, options);
-        JSScript *script = JS_CompileUCScript(cx, global, codeChars, codeLength, fileName, lineNumber);
+        RootedScript script(cx, JS_CompileUCScript(cx, global, codeChars, codeLength, fileName, lineNumber));
         JS_SetOptions(cx, saved);
         if (!script)
             return false;
 
         if (sourceMapURL && !script->scriptSource()->hasSourceMap()) {
             if (!script->scriptSource()->setSourceMap(cx, sourceMapURL, script->filename))
                 return false;
         }
@@ -1196,17 +1196,17 @@ Run(JSContext *cx, unsigned argc, jsval 
     if (!ucbuf)
         return false;
 
     JS::Anchor<JSString *> a_str(str);
     uint32_t oldopts = JS_GetOptions(cx);
     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
 
     int64_t startClock = PRMJ_Now();
-    JSScript *script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, filename.ptr(), 1);
+    RootedScript script(cx, JS_CompileUCScript(cx, thisobj, ucbuf, buflen, filename.ptr(), 1));
     JS_SetOptions(cx, oldopts);
     if (!script || !JS_ExecuteScript(cx, thisobj, script, NULL))
         return false;
 
     int64_t endClock = PRMJ_Now();
     JS_SET_RVAL(cx, vp, DOUBLE_TO_JSVAL((endClock - startClock) / double(PRMJ_USEC_PER_MSEC)));
     return true;
 }
@@ -1445,22 +1445,22 @@ AssertJit(JSContext *cx, unsigned argc, 
         }
     }
 #endif
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
-static JSScript *
+static UnrootedScript
 ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL)
 {
     RootedFunction fun(cx, JS_ValueToFunction(cx, v));
     if (!fun)
-        return NULL;
+        return UnrootedScript(NULL);
 
     RootedScript script(cx);
     fun->maybeGetOrCreateScript(cx, &script);
     if (!script)
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_SCRIPTS_ONLY);
 
     if (fun && funp)
         *funp = fun;
@@ -1486,29 +1486,29 @@ SetDebug(JSContext *cx, unsigned argc, j
      */
 
     bool ok = !!JS_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0]));
     if (ok)
         JS_SET_RVAL(cx, vp, JSVAL_TRUE);
     return ok;
 }
 
-static JSScript *
+static UnrootedScript
 GetTopScript(JSContext *cx)
 {
-    JSScript *script;
-    JS_DescribeScriptedCaller(cx, &script, NULL);
+    RootedScript script(cx);
+    JS_DescribeScriptedCaller(cx, script.address(), NULL);
     return script;
 }
 
 static JSBool
-GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, JSScript **scriptp,
+GetScriptAndPCArgs(JSContext *cx, unsigned argc, jsval *argv, MutableHandleScript scriptp,
                    int32_t *ip)
 {
-    JSScript *script = GetTopScript(cx);
+    RootedScript script(cx, GetTopScript(cx));
     *ip = 0;
     if (argc != 0) {
         jsval v = argv[0];
         unsigned intarg = 0;
         if (!JSVAL_IS_PRIMITIVE(v) &&
             JS_GetClass(JSVAL_TO_OBJECT(v)) == Jsvalify(&FunctionClass)) {
             script = ValueToScript(cx, v);
             if (!script)
@@ -1520,23 +1520,23 @@ GetScriptAndPCArgs(JSContext *cx, unsign
                 return false;
             if ((uint32_t)*ip >= script->length) {
                 JS_ReportError(cx, "Invalid PC");
                 return false;
             }
         }
     }
 
-    *scriptp = script;
+    scriptp.set(script);
 
     return true;
 }
 
 static JSTrapStatus
-TrapHandler(JSContext *cx, JSScript *, jsbytecode *pc, jsval *rval,
+TrapHandler(JSContext *cx, RawScript, jsbytecode *pc, jsval *rval,
             jsval closure)
 {
     JSString *str = JSVAL_TO_STRING(closure);
 
     ScriptFrameIter iter(cx);
     JS_ASSERT(!iter.done());
 
     /* Debug-mode currently disables Ion compilation. */
@@ -1558,17 +1558,17 @@ TrapHandler(JSContext *cx, JSScript *, j
         return JSTRAP_RETURN;
     return JSTRAP_CONTINUE;
 }
 
 static JSBool
 Trap(JSContext *cx, unsigned argc, jsval *vp)
 {
     JSString *str;
-    JSScript *script;
+    RootedScript script(cx);
     int32_t i;
 
     jsval *argv = JS_ARGV(cx, vp);
     if (argc == 0) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);
         return false;
     }
     argc--;
@@ -1584,28 +1584,28 @@ Trap(JSContext *cx, unsigned argc, jsval
     }
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_SetTrap(cx, script, script->code + i, TrapHandler, STRING_TO_JSVAL(str));
 }
 
 static JSBool
 Untrap(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JSScript *script;
+    RootedScript script(cx);
     int32_t i;
 
     if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
         return false;
     JS_ClearTrap(cx, script, script->code + i, NULL, NULL);
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 static JSTrapStatus
-DebuggerAndThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
+DebuggerAndThrowHandler(JSContext *cx, RawScript script, jsbytecode *pc, jsval *rval,
                         void *closure)
 {
     return TrapHandler(cx, script, pc, rval, STRING_TO_JSVAL((JSString *)closure));
 }
 
 static JSBool
 SetDebuggerHandler(JSContext *cx, unsigned argc, jsval *vp)
 {
@@ -1673,33 +1673,33 @@ LineToPC(JSContext *cx, unsigned argc, j
         return false;
     *vp = INT_TO_JSVAL(pc - script->code);
     return true;
 }
 
 static JSBool
 PCToLine(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JSScript *script;
+    RootedScript script(cx);
     int32_t i;
     unsigned lineno;
 
     if (!GetScriptAndPCArgs(cx, argc, JS_ARGV(cx, vp), &script, &i))
         return false;
     lineno = JS_PCToLineNumber(cx, script, script->code + i);
     if (!lineno)
         return false;
     *vp = INT_TO_JSVAL(lineno);
     return true;
 }
 
 #ifdef DEBUG
 
 static void
-UpdateSwitchTableBounds(JSContext *cx, JSScript *script, unsigned offset,
+UpdateSwitchTableBounds(JSContext *cx, HandleScript script, unsigned offset,
                         unsigned *start, unsigned *end)
 {
     jsbytecode *pc;
     JSOp op;
     ptrdiff_t jmplen;
     int32_t low, high, n;
 
     pc = script->code + offset;
@@ -1729,17 +1729,17 @@ UpdateSwitchTableBounds(JSContext *cx, J
         return;
     }
 
     *start = (unsigned)(pc - script->code);
     *end = *start + (unsigned)(n * jmplen);
 }
 
 static void
-SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
+SrcNotes(JSContext *cx, HandleScript script, Sprinter *sp)
 {
     Sprint(sp, "\nSource notes:\n");
     Sprint(sp, "%4s  %4s %5s %6s %-8s %s\n",
            "ofs", "line", "pc", "delta", "desc", "args");
     Sprint(sp, "---- ---- ----- ------ -------- ------\n");
     unsigned offset = 0;
     unsigned colspan = 0;
     unsigned lineno = script->lineno;
@@ -1846,17 +1846,17 @@ static JSBool
 Notes(JSContext *cx, unsigned argc, jsval *vp)
 {
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
 
     jsval *argv = JS_ARGV(cx, vp);
     for (unsigned i = 0; i < argc; i++) {
-        JSScript *script = ValueToScript(cx, argv[i]);
+        RootedScript script (cx, ValueToScript(cx, argv[i]));
         if (!script)
             return false;
 
         SrcNotes(cx, script, &sprinter);
     }
 
     JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
     if (!str)
@@ -1867,17 +1867,17 @@ Notes(JSContext *cx, unsigned argc, jsva
 
 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
 JS_STATIC_ASSERT(JSTRY_ITER == 2);
 
 static const char* const TryNoteNames[] = { "catch", "finally", "iter" };
 
 static JSBool
-TryNotes(JSContext *cx, JSScript *script, Sprinter *sp)
+TryNotes(JSContext *cx, HandleScript script, Sprinter *sp)
 {
     JSTryNote *tn, *tnlimit;
 
     if (!script->hasTrynotes())
         return true;
 
     tn = script->trynotes()->vector;
     tnlimit = tn + script->trynotes()->length;
@@ -1887,21 +1887,19 @@ TryNotes(JSContext *cx, JSScript *script
         Sprint(sp, " %-7s %6u %8u %8u\n",
                TryNoteNames[tn->kind], tn->stackDepth,
                tn->start, tn->start + tn->length);
     } while (++tn != tnlimit);
     return true;
 }
 
 static bool
-DisassembleScript(JSContext *cx, JSScript *script_, JSFunction *fun, bool lines, bool recursive,
+DisassembleScript(JSContext *cx, HandleScript script, JSFunction *fun, bool lines, bool recursive,
                   Sprinter *sp)
 {
-    Rooted<JSScript*> script(cx, script_);
-
     if (fun) {
         Sprint(sp, "flags:");
         if (fun->isLambda())
             Sprint(sp, " LAMBDA");
         if (fun->isHeavyweight())
             Sprint(sp, " HEAVYWEIGHT");
         if (fun->isExprClosure())
             Sprint(sp, " EXPRESSION CLOSURE");
@@ -1923,17 +1921,17 @@ DisassembleScript(JSContext *cx, JSScrip
         ObjectArray *objects = script->objects();
         for (unsigned i = 0; i != objects->length; ++i) {
             RawObject obj = objects->vector[i];
             if (obj->isFunction()) {
                 Sprint(sp, "\n");
                 RawFunction fun = obj->toFunction();
                 RootedScript script(cx);
                 fun->maybeGetOrCreateScript(cx, &script);
-                if (!DisassembleScript(cx, script.get(), fun, lines, recursive, sp))
+                if (!DisassembleScript(cx, script, fun, lines, recursive, sp))
                     return false;
             }
         }
     }
     return true;
 }
 
 namespace {
@@ -1982,17 +1980,17 @@ DisassembleToSprinter(JSContext *cx, uns
             if (!js_Disassemble(cx, script, p.lines, sprinter))
                 return false;
             SrcNotes(cx, script, sprinter);
             TryNotes(cx, script, sprinter);
         }
     } else {
         for (unsigned i = 0; i < p.argc; i++) {
             JSFunction *fun;
-            JSScript *script = ValueToScript(cx, p.argv[i], &fun);
+            RootedScript script (cx, ValueToScript(cx, p.argv[i], &fun));
             if (!script)
                 return false;
             if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter))
                 return false;
         }
     }
     return true;
 }
@@ -2051,17 +2049,17 @@ DisassFile(JSContext *cx, unsigned argc,
     if (!filename)
         return false;
 
     uint32_t oldopts = JS_GetOptions(cx);
     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
     CompileOptions options(cx);
     options.setUTF8(true)
            .setFileAndLine(filename.ptr(), 1);
-    JSScript *script = JS::Compile(cx, thisobj, options, filename.ptr());
+    RootedScript script (cx, JS::Compile(cx, thisobj, options, filename.ptr()));
     JS_SetOptions(cx, oldopts);
     if (!script)
         return false;
 
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter);
@@ -2424,17 +2422,17 @@ GetPDA(JSContext *cx, unsigned argc, jsv
     }
     JS_PutPropertyDescArray(cx, &pda);
     return ok;
 }
 
 static JSBool
 GetSLX(JSContext *cx, unsigned argc, jsval *vp)
 {
-    JSScript *script;
+    RootedScript script(cx);
 
     script = ValueToScript(cx, argc == 0 ? JSVAL_VOID : vp[2]);
     if (!script)
         return false;
     *vp = INT_TO_JSVAL(js_GetScriptLineExtent(script));
     return true;
 }
 
@@ -2603,20 +2601,20 @@ EvalInContext(JSContext *cx, unsigned ar
             return false;
     }
 
     if (srclen == 0) {
         JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(sobj));
         return true;
     }
 
-    JSScript *script;
+    RootedScript script(cx);
     unsigned lineno;
 
-    JS_DescribeScriptedCaller(cx, &script, &lineno);
+    JS_DescribeScriptedCaller(cx, script.address(), &lineno);
     jsval rval;
     {
         Maybe<JSAutoCompartment> ac;
         unsigned flags;
         JSObject *unwrapped = UnwrapObject(sobj, true, &flags);
         if (flags & Wrapper::CROSS_COMPARTMENT) {
             sobj = unwrapped;
             ac.construct(cx, sobj);
@@ -3309,17 +3307,17 @@ Snarf(JSContext *cx, unsigned argc, jsva
     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
     if (!str)
         return false;
     JSAutoByteString filename(cx, str);
     if (!filename)
         return false;
 
     /* Get the currently executing script's name. */
-    JSScript *script = GetTopScript(cx);
+    RootedScript script(cx, GetTopScript(cx));
     JS_ASSERT(script->filename);
     const char *pathname = filename.ptr();
 #ifdef XP_UNIX
     FreeOnReturn pnGuard(cx);
     if (pathname[0] != '/') {
         pathname = MakeAbsolutePathname(cx, script->filename, pathname);
         if (!pathname)
             return false;
@@ -3376,34 +3374,34 @@ DecompileFunction(JSContext *cx, unsigne
 {
     return DecompileFunctionSomehow(cx, argc, vp, JS_DecompileFunction);
 }
 
 static JSBool
 DecompileThisScript(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSScript *script = NULL;
-    if (!JS_DescribeScriptedCaller(cx, &script, NULL)) {
+    RootedScript script (cx);
+    if (!JS_DescribeScriptedCaller(cx, script.address(), NULL)) {
         args.rval().setString(cx->runtime->emptyString);
         return true;
     }
     JSString *result = JS_DecompileScript(cx, script, "test", 0);
     if (!result)
         return false;
     args.rval().setString(result);
     return true;
 }
 
 static JSBool
 ThisFilename(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSScript *script = NULL;
-    if (!JS_DescribeScriptedCaller(cx, &script, NULL) || !script->filename) {
+    RootedScript script (cx);
+    if (!JS_DescribeScriptedCaller(cx, script.address(), NULL) || !script->filename) {
         args.rval().setString(cx->runtime->emptyString);
         return true;
     }
     JSString *filename = JS_NewStringCopyZ(cx, script->filename);
     if (!filename)
         return false;
     args.rval().setString(filename);
     return true;
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -89,16 +89,17 @@ enum ParallelResult { TP_SUCCESS, TP_RET
 struct ForkJoinOp;
 
 // Executes the given |TaskSet| in parallel using the runtime's |ThreadPool|,
 // returning upon completion.  In general, if there are |N| workers in the
 // threadpool, the problem will be divided into |N+1| slices, as the main
 // thread will also execute one slice.
 ParallelResult ExecuteForkJoinOp(JSContext *cx, ForkJoinOp &op);
 
+class PerThreadData;
 class ForkJoinShared;
 class AutoRendezvous;
 class AutoSetForkJoinSlice;
 namespace gc { struct ArenaLists; }
 
 struct ForkJoinSlice
 {
   public:
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -401,17 +401,17 @@ GlobalObject::initFunctionAndObjectClass
     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.
      */
     RootedScript functionProtoScript(cx, functionProto->nonLazyScript());
-    js_CallNewScriptHook(cx, functionProtoScript, functionProto);
+    CallNewScriptHook(cx, functionProtoScript, functionProto);
     return functionProto;
 }
 
 GlobalObject *
 GlobalObject::create(JSContext *cx, Class *clasp)
 {
     JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -255,16 +255,17 @@ js::ObjectImpl::slotInRange(uint32_t slo
  * causes stack pointer offsets to go awry and spp to refer to something higher
  * up the stack.
  */
 MOZ_NEVER_INLINE
 #endif
 UnrootedShape
 js::ObjectImpl::nativeLookup(JSContext *cx, jsid idArg)
 {
+    AssertCanGC();
     MOZ_ASSERT(isNative());
     Shape **spp;
     RootedId id(cx, idArg);
     return Shape::search(cx, lastProperty(), id, &spp);
 }
 
 UnrootedShape
 js::ObjectImpl::nativeLookupNoAllocation(jsid id)
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -366,17 +366,17 @@ SPSProfiler::discardMJITCode(mjit::JITSc
         return;
 
     unregisterScript(jscr->script, chunk);
     for (unsigned i = 0; i < chunk->nInlineFrames; i++)
         unregisterScript(chunk->inlineFrames()[i].fun->nonLazyScript(), chunk);
 }
 
 void
-SPSProfiler::unregisterScript(JSScript *script, mjit::JITChunk *chunk)
+SPSProfiler::unregisterScript(UnrootedScript script, mjit::JITChunk *chunk)
 {
     JITInfoMap::Ptr ptr = jminfo.lookup(script);
     if (!ptr)
         return;
     JMScriptInfo *info = ptr->value;
     for (unsigned i = 0; i < info->chunks.length(); i++) {
         if (info->chunks[i].chunk == chunk) {
             info->chunks.erase(&info->chunks[i]);
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -233,17 +233,17 @@ class SPSProfiler
     bool registerICCode(mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
                         void *start, size_t size);
     jsbytecode *ipToPC(JSScript *script, size_t ip);
 
   private:
     JMChunkInfo *registerScript(mjit::JSActiveFrame *frame,
                                 mjit::PCLengthEntry *lenths,
                                 mjit::JITChunk *chunk);
-    void unregisterScript(JSScript *script, mjit::JITChunk *chunk);
+    void unregisterScript(UnrootedScript script, mjit::JITChunk *chunk);
   public:
 #else
     jsbytecode *ipToPC(JSScript *script, size_t ip) { return NULL; }
 #endif
 
     void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
     const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun);
     void onScriptFinalized(JSScript *script);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -153,17 +153,17 @@ CallObject::create(JSContext *cx, Handle
 }
 
 /*
  * Create a CallObject for a JSScript that is not initialized to any particular
  * callsite. This object can either be initialized (with an enclosing scope and
  * callee) or used as a template for jit compilation.
  */
 CallObject *
-CallObject::createTemplateObject(JSContext *cx, JSScript *script)
+CallObject::createTemplateObject(JSContext *cx, HandleScript script)
 {
     RootedShape shape(cx, script->bindings.callObjShape());
 
     RootedTypeObject type(cx, cx->compartment->getNewType(cx, NULL));
     if (!type)
         return NULL;
 
     HeapSlot *slots;
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -184,17 +184,17 @@ class CallObject : public ScopeObject
     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);
+    createTemplateObject(JSContext *cx, HandleScript script);
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);
 
     static CallObject *createForFunction(JSContext *cx, StackFrame *fp);
     static CallObject *createForStrictEval(JSContext *cx, StackFrame *fp);
 
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -127,19 +127,18 @@ StackFrame::resetInlinePrev(StackFrame *
     flags_ |= StackFrame::HAS_PREVPC;
     prev_ = prevfp;
     prevpc_ = prevpc;
     prevInline_ = NULL;
 }
 
 inline void
 StackFrame::initCallFrame(JSContext *cx, JSFunction &callee,
-                          JSScript *script, uint32_t nactual, StackFrame::Flags flagsArg)
+                          UnrootedScript script, uint32_t nactual, StackFrame::Flags flagsArg)
 {
-    AutoAssertNoGC nogc;
     JS_ASSERT((flagsArg & ~(CONSTRUCTING |
                             LOWERED_CALL_APPLY |
                             OVERFLOW_ARGS |
                             UNDERFLOW_ARGS)) == 0);
     JS_ASSERT(callee.nonLazyScript() == script);
 
     /* Initialize stack frame members. */
     flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg;
@@ -410,17 +409,17 @@ StackSpace::getStackLimit(JSContext *cx,
            ? conservativeEnd_
            : NULL;
 }
 
 /*****************************************************************************/
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
-                           JSFunction *fun, JSScript *script, StackFrame::Flags *flags) const
+                           JSFunction *fun, HandleScript script, StackFrame::Flags *flags) const
 {
     mozilla::Maybe<AutoAssertNoGC> maybeNoGC;
     if (report)
         AssertCanGC();
     else
         maybeNoGC.construct();
 
     JS_ASSERT(fun->nonLazyScript() == script);
@@ -456,17 +455,17 @@ ContextStack::getCallFrame(JSContext *cx
     Value *dst = firstUnused;
     Value *src = args.base();
     PodCopy(dst, src, ncopy);
     return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
 }
 
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                              JSFunction &callee, JSScript *script,
+                              JSFunction &callee, HandleScript script,
                               InitialFrameFlags initial, MaybeReportError report)
 {
     mozilla::Maybe<AutoAssertNoGC> maybeNoGC;
     if (report)
         AssertCanGC();
     else
         maybeNoGC.construct();
 
@@ -488,29 +487,29 @@ ContextStack::pushInlineFrame(JSContext 
      * to repoint the active registers to regs. See UncachedInlineCall.
      */
     regs.prepareToRun(*fp, script);
     return true;
 }
 
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                              JSFunction &callee, JSScript *script,
+                              JSFunction &callee, HandleScript 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,
+                            const CallArgs &args, JSFunction *fun, HandleScript script,
                             void *ncode, InitialFrameFlags initial, Value **stackLimit)
 {
     AssertCanGC();
     JS_ASSERT(onTop());
     JS_ASSERT(fun->nonLazyScript() == args.callee().toFunction()->nonLazyScript());
     JS_ASSERT(fun->nonLazyScript() == script);
 
     StackFrame::Flags flags = ToFrameFlags(initial);
@@ -543,17 +542,17 @@ inline void
 ContextStack::popFrameAfterOverflow()
 {
     /* Restore the regs to what they were on entry to JSOP_CALL. */
     FrameRegs &regs = seg_->regs();
     StackFrame *fp = regs.fp();
     regs.popFrame(fp->actuals() + fp->numActualArgs());
 }
 
-inline JSScript *
+inline UnrootedScript
 ContextStack::currentScript(jsbytecode **ppc,
                             MaybeAllowCrossCompartment allowCrossCompartment) const
 {
     AutoAssertNoGC nogc;
 
     if (ppc)
         *ppc = NULL;
 
@@ -563,39 +562,39 @@ ContextStack::currentScript(jsbytecode *
     FrameRegs &regs = this->regs();
     StackFrame *fp = regs.fp();
 
 #ifdef JS_ION
     if (fp->beginsIonActivation()) {
         RootedScript script(cx_);
         ion::GetPcScript(cx_, &script, ppc);
         if (!allowCrossCompartment && script->compartment() != cx_->compartment)
-            return NULL;
+            return UnrootedScript(NULL);
         return script;
     }
 #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];
         UnrootedScript script = frame->fun->nonLazyScript();
         if (!allowCrossCompartment && script->compartment() != cx_->compartment)
-            return NULL;
+            return UnrootedScript(NULL);
         if (ppc)
             *ppc = script->code + inlined->pcOffset;
         return script;
     }
 #endif
 
     UnrootedScript script = fp->script();
     if (!allowCrossCompartment && script->compartment() != cx_->compartment)
-        return NULL;
+        return UnrootedScript(NULL);
 
     if (ppc)
         *ppc = fp->pcQuadratic(*this);
     return script;
 }
 
 inline HandleObject
 ContextStack::currentScriptedScopeChain() const
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -43,17 +43,17 @@
 
 using namespace js;
 
 using mozilla::DebugOnly;
 
 /*****************************************************************************/
 
 void
-StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
+StackFrame::initExecuteFrame(UnrootedScript script, StackFrame *prev, FrameRegs *regs,
                              const Value &thisv, JSObject &scopeChain, ExecuteType type)
 {
     /*
      * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
      * script in the context of another frame and the frame type is determined
      * by the context.
      */
     flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC;
@@ -67,17 +67,17 @@ StackFrame::initExecuteFrame(JSScript *s
         dstvp[0] = prev->calleev();
         exec = prev->exec;
         u.evalScript = script;
     } else {
         JS_ASSERT(isGlobalFrame());
         dstvp[0] = NullValue();
         exec.script = script;
 #ifdef DEBUG
-        u.evalScript = (JSScript *)0xbad;
+        u.evalScript = (RawScript)0xbad;
 #endif
     }
 
     scopeChain_ = &scopeChain;
     prev_ = prev;
     prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
     prevInline_ = regs ? regs->inlined() : NULL;
     blockChain_ = NULL;
@@ -1239,17 +1239,17 @@ ContextStack::restoreFrameChain()
 }
 
 /*****************************************************************************/
 
 void
 StackIter::poisonRegs()
 {
     pc_ = (jsbytecode *)0xbad;
-    script_ = (JSScript *)0xbad;
+    script_ = (RawScript)0xbad;
 }
 
 void
 StackIter::popFrame()
 {
     AutoAssertNoGC nogc;
     StackFrame *oldfp = fp_;
     JS_ASSERT(seg_->contains(oldfp));
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -277,22 +277,22 @@ class StackFrame
         /* Ion frame state */
         RUNNING_IN_ION       = 0x400000,  /* frame is running in Ion */
         CALLING_INTO_ION     = 0x800000   /* frame is calling into Ion */
     };
 
   private:
     mutable uint32_t    flags_;         /* bits described by Flags */
     union {                             /* describes what code is executing in a */
-        JSScript        *script;        /*   global frame */
+        RawScript       script;         /*   global frame */
         JSFunction      *fun;           /*   function frame, pre GetScopeChain */
     } exec;
     union {                             /* describes the arguments of a function */
         unsigned        nactual;        /*   for non-eval frames */
-        JSScript        *evalScript;    /*   the script of an eval-in-function */
+        RawScript       evalScript;     /*   the script of an eval-in-function */
     } u;
     mutable JSObject    *scopeChain_;   /* if HAS_SCOPECHAIN, current scope chain */
     StackFrame          *prev_;         /* if HAS_PREVPC, previous cx->regs->fp */
     void                *ncode_;        /* for a jit frame, return address for method JIT */
     Value               rval_;          /* if HAS_RVAL, return value of the frame */
     StaticBlockObject   *blockChain_;   /* if HAS_BLOCKCHAIN, innermost let block */
     ArgumentsObject     *argsObj_;      /* if HAS_ARGS_OBJ, the call's arguments object */
     jsbytecode          *prevpc_;       /* if HAS_PREVPC, pc of previous frame*/
@@ -340,23 +340,23 @@ class StackFrame
 
     /*
      * Frame initialization, called by ContextStack operations after acquiring
      * the raw memory for the frame:
      */
 
     /* Used for Invoke, Interpret, trace-jit LeaveTree, and method-jit stubs. */
     void initCallFrame(JSContext *cx, JSFunction &callee,
-                       JSScript *script, uint32_t nactual, StackFrame::Flags flags);
+                       UnrootedScript script, uint32_t nactual, StackFrame::Flags flags);
 
     /* Used for getFixupFrame (for FixupArity). */
     void initFixupFrame(StackFrame *prev, StackFrame::Flags flags, void *ncode, unsigned nactual);
 
     /* Used for eval. */
-    void initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
+    void initExecuteFrame(UnrootedScript script, StackFrame *prev, FrameRegs *regs,
                           const Value &thisv, JSObject &scopeChain, ExecuteType type);
 
   public:
     /*
      * Frame prologue/epilogue
      *
      * Every stack frame must have 'prologue' called before executing the
      * first op and 'epilogue' called after executing the last op and before
@@ -1187,17 +1187,17 @@ class FrameRegs
     }
 
     /* For EnterMethodJIT: */
     void refreshFramePointer(StackFrame *fp) {
         fp_ = fp;
     }
 
     /* For stubs::CompileFunction, ContextStack: */
-    void prepareToRun(StackFrame &fp, JSScript *script) {
+    void prepareToRun(StackFrame &fp, UnrootedScript script) {
         pc = script->code;
         sp = fp.slots() + script->nfixed;
         fp_ = &fp;
         inlined_ = NULL;
     }
 
     void setToEndOfScript() {
         UnrootedScript script = fp()->script();
@@ -1484,17 +1484,17 @@ class ContextStack
     /* Implementation details of push* public interface. */
     StackSegment *pushSegment(JSContext *cx);
     enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
     Value *ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
                        MaybeExtend extend, bool *pushedSeg);
 
     inline StackFrame *
     getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
-                 JSFunction *fun, JSScript *script, StackFrame::Flags *pflags) const;
+                 JSFunction *fun, HandleScript script, StackFrame::Flags *pflags) const;
 
     /* Make pop* functions private since only called by guard classes. */
     void popSegment();
     friend class InvokeArgsGuard;
     void popInvokeArgs(const InvokeArgsGuard &iag);
     friend class FrameGuard;
     void popFrame(const FrameGuard &fg);
     friend class GeneratorFrameGuard;
@@ -1585,50 +1585,50 @@ class ContextStack
     bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
 
     /*
      * An "inline frame" may only be pushed from within the top, active
      * segment. This is the case for calls made inside mjit code and Interpret.
      * The 'stackLimit' overload updates 'stackLimit' if it changes.
      */
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                         JSFunction &callee, JSScript *script,
+                         JSFunction &callee, HandleScript script,
                          InitialFrameFlags initial,
                          MaybeReportError report = REPORT_ERROR);
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                         JSFunction &callee, JSScript *script,
+                         JSFunction &callee, HandleScript script,
                          InitialFrameFlags initial, Value **stackLimit);
     void popInlineFrame(FrameRegs &regs);
 
     /* Pop a partially-pushed frame after hitting the limit before throwing. */
     void popFrameAfterOverflow();
 
     /*
      * Get the topmost script and optional pc on the stack. By default, this
      * function only returns a JSScript in the current compartment, returning
      * NULL if the current script is in a different compartment. This behavior
      * can be overridden by passing ALLOW_CROSS_COMPARTMENT.
      */
     enum MaybeAllowCrossCompartment {
         DONT_ALLOW_CROSS_COMPARTMENT = false,
         ALLOW_CROSS_COMPARTMENT = true
     };
-    inline JSScript *currentScript(jsbytecode **pc = NULL,
-                                   MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const;
+    inline UnrootedScript currentScript(jsbytecode **pc = NULL,
+                                        MaybeAllowCrossCompartment = DONT_ALLOW_CROSS_COMPARTMENT) const;
 
     /* Get the scope chain for the topmost scripted call on the stack. */
     inline HandleObject currentScriptedScopeChain() const;
 
     /*
      * Called by the methodjit for an arity mismatch. Arity mismatch can be
      * hot, so getFixupFrame avoids doing call setup performed by jit code when
      * FixupArity returns.
      */
     StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
-                              const CallArgs &args, JSFunction *fun, JSScript *script,
+                              const CallArgs &args, JSFunction *fun, HandleScript script,
                               void *ncode, InitialFrameFlags initial, Value **stackLimit);
 
     bool saveFrameChain();
     void restoreFrameChain();
 
     /*
      * As an optimization, the interpreter/mjit can operate on a local
      * FrameRegs instance repoint the ContextStack to this local instance.
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -143,17 +143,17 @@ XDRState<mode>::codeScript(MutableHandle
     if (!VersionCheck(this))
         return false;
 
     if (!XDRScript(this, NullPtr(), NullPtr(), NullPtr(), &script))
         return false;
 
     if (mode == XDR_DECODE) {
         JS_ASSERT(!script->compileAndGo);
-        js_CallNewScriptHook(cx(), script, NULL);
+        CallNewScriptHook(cx(), script, NullPtr());
         Debugger::onNewScript(cx(), script, NULL);
         scriptp.set(script);
     }
 
     return true;
 }
 
 XDRDecoder::XDRDecoder(JSContext *cx, const void *data, uint32_t length,