Bug 885758 - Add ExclusiveContext for use by threads with exclusive access to their compartment, r=billm.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 10 Jul 2013 09:29:52 -0600
changeset 137995 b674f0e40c8eb12edb964b80aa4ca2af37fcbf4c
parent 137994 b7d6458d2a3cc8a7fa5417400d5a41355dd95c84
child 137996 6efe5b6904d0694ae63ecdd2a991c6362f8eb76c
push id24941
push useremorley@mozilla.com
push dateThu, 11 Jul 2013 09:11:18 +0000
treeherdermozilla-central@c89e5b9fe935 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs885758
milestone25.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 885758 - Add ExclusiveContext for use by threads with exclusive access to their compartment, r=billm.
js/public/RootingAPI.h
js/src/builtin/Module.cpp
js/src/builtin/Module.h
js/src/builtin/Object.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseMaps-inl.h
js/src/frontend/ParseMaps.cpp
js/src/frontend/ParseMaps.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext-inl.h
js/src/frontend/SharedContext.h
js/src/frontend/SyntaxParseHandler.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/gc/RootMarking.cpp
js/src/gdb/tests/test-JSString.cpp
js/src/ion/AsmJS.cpp
js/src/ion/AsmJSLink.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonMacroAssembler.cpp
js/src/ion/ParallelFunctions.cpp
js/src/jsalloc.cpp
js/src/jsalloc.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsatominlines.h
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jspropertytree.cpp
js/src/jspropertytree.h
js/src/jsproxy.cpp
js/src/jspubtd.h
js/src/jsreflect.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/shell/js.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/ObjectImpl-inl.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/Probes.h
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics-inl.h
js/src/vm/Runtime.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/Shape-inl.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/String-inl.h
js/src/vm/String.cpp
js/src/vm/String.h
js/src/vm/TypedArrayObject.cpp
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -507,17 +507,17 @@ struct GCMethods<T *>
         JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell **>(vp));
     }
     static void relocate(T **vp) {
         JS::HeapCellRelocate(reinterpret_cast<js::gc::Cell **>(vp));
     }
 #endif
 };
 
-#if defined(DEBUG) && defined(JS_THREADSAFE)
+#if defined(DEBUG)
 /* This helper allows us to assert that Rooted<T> is scoped within a request. */
 extern JS_PUBLIC_API(bool)
 IsInRequest(JSContext *cx);
 #endif
 
 } /* namespace js */
 
 namespace JS {
@@ -528,65 +528,78 @@ namespace JS {
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
  *
  * If you want to add additional methods to Rooted for a specific
  * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
 class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
 {
-    void init(JSContext *cxArg) {
-        MOZ_ASSERT(cxArg);
-#ifdef JS_THREADSAFE
-        MOZ_ASSERT(js::IsInRequest(cxArg));
-#endif
+    /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
+    template <typename CX>
+    void init(CX *cx) {
 #ifdef JSGC_TRACK_EXACT_ROOTS
-        js::ContextFriendFields *cx = js::ContextFriendFields::get(cxArg);
-        commonInit(cx->thingGCRooters);
-#endif
-    }
+        js::ThingRootKind kind = js::GCMethods<T>::kind();
+        this->stack = &cx->thingGCRooters[kind];
+        this->prev = *stack;
+        *stack = reinterpret_cast<Rooted<void*>*>(this);
 
-    void init(js::PerThreadDataFriendFields *pt) {
-        MOZ_ASSERT(pt);
-#ifdef JSGC_TRACK_EXACT_ROOTS
-        commonInit(pt->thingGCRooters);
+        JS_ASSERT(!js::GCMethods<T>::poisoned(ptr));
 #endif
     }
 
   public:
     Rooted(JSContext *cx
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        MOZ_ASSERT(js::IsInRequest(cx));
+        init(js::ContextFriendFields::get(cx));
+    }
+
+    Rooted(JSContext *cx, T initial
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(initial)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        MOZ_ASSERT(js::IsInRequest(cx));
+        init(js::ContextFriendFields::get(cx));
+    }
+
+    Rooted(js::ContextFriendFields *cx
+           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : ptr(js::GCMethods<T>::initial())
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
-    Rooted(JSContext *cx, T initial
+    Rooted(js::ContextFriendFields *cx, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(cx);
     }
 
-    Rooted(js::PerThreadData *pt
+    Rooted(js::PerThreadDataFriendFields *pt
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::PerThreadDataFriendFields::get(pt));
+        init(pt);
     }
 
-    Rooted(js::PerThreadData *pt, T initial
+    Rooted(js::PerThreadDataFriendFields *pt, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        init(js::PerThreadDataFriendFields::get(pt));
+        init(pt);
     }
 
     Rooted(JSRuntime *rt
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         init(js::PerThreadDataFriendFields::getMainThread(rt));
@@ -637,30 +650,16 @@ class MOZ_STACK_CLASS Rooted : public js
         JS_ASSERT(!js::GCMethods<T>::poisoned(value));
         ptr = value;
     }
 
     bool operator!=(const T &other) const { return ptr != other; }
     bool operator==(const T &other) const { return ptr == other; }
 
   private:
-    void commonInit(Rooted<void*> **thingGCRooters) {
-#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
-        scanned = false;
-#endif
-#ifdef JSGC_TRACK_EXACT_ROOTS
-        js::ThingRootKind kind = js::GCMethods<T>::kind();
-        this->stack = &thingGCRooters[kind];
-        this->prev = *stack;
-        *stack = reinterpret_cast<Rooted<void*>*>(this);
-
-        JS_ASSERT(!js::GCMethods<T>::poisoned(ptr));
-#endif
-    }
-
 #ifdef JSGC_TRACK_EXACT_ROOTS
     Rooted<void*> **stack, *prev;
 #endif
 
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
     /* Has the rooting analysis ever scanned this Rooted's stack location? */
     friend void JS::CheckStackRoots(JSContext*);
 #endif
@@ -708,90 +707,90 @@ namespace js {
 class SkipRoot
 {
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 
     SkipRoot **stack, *prev;
     const uint8_t *start;
     const uint8_t *end;
 
-    template <typename T>
-    void init(SkipRoot **head, const T *ptr, size_t count) {
+    template <typename CX, typename T>
+    void init(CX *cx, const T *ptr, size_t count) {
+        SkipRoot **head = &cx->skipGCRooters;
         this->stack = head;
         this->prev = *stack;
         *stack = this;
         this->start = (const uint8_t *) ptr;
         this->end = this->start + (sizeof(T) * count);
     }
 
   public:
-    template <typename T>
-    SkipRoot(JSContext *cx, const T *ptr, size_t count = 1
-             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    {
-        init(&ContextFriendFields::get(cx)->skipGCRooters, ptr, count);
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    template <typename T>
-    SkipRoot(js::PerThreadData *ptd, const T *ptr, size_t count = 1
-             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    {
-        PerThreadDataFriendFields *ptff = PerThreadDataFriendFields::get(ptd);
-        init(&ptff->skipGCRooters, ptr, count);
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
     ~SkipRoot() {
         JS_ASSERT(*stack == this);
         *stack = prev;
     }
 
     SkipRoot *previous() { return prev; }
 
     bool contains(const uint8_t *v, size_t len) {
         return v >= start && v + len <= end;
     }
 
 #else /* DEBUG && JSGC_ROOT_ANALYSIS */
 
+    template <typename T>
+    void init(js::ContextFriendFields *cx, const T *ptr, size_t count) {}
+
   public:
+
+#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
+
     template <typename T>
     SkipRoot(JSContext *cx, const T *ptr, size_t count = 1
              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
+        init(ContextFriendFields::get(cx), ptr, count);
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     template <typename T>
-    SkipRoot(PerThreadData *ptd, const T *ptr, size_t count = 1
+    SkipRoot(ContextFriendFields *cx, const T *ptr, size_t count = 1
              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
+        init(cx, ptr, count);
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
+    template <typename T>
+    SkipRoot(PerThreadData *pt, const T *ptr, size_t count = 1
+             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        init(PerThreadDataFriendFields::get(pt), ptr, count);
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /* Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
 class FakeRooted : public RootedBase<T>
 {
   public:
-    FakeRooted(JSContext *cx
-                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    template <typename CX>
+    FakeRooted(CX *cx
+               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-    FakeRooted(JSContext *cx, T initial
-                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    template <typename CX>
+    FakeRooted(CX *cx, T initial
+               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     operator T() const { return ptr; }
     T operator->() const { return ptr; }
     T *address() { return &ptr; }
--- a/js/src/builtin/Module.cpp
+++ b/js/src/builtin/Module.cpp
@@ -30,17 +30,17 @@ Module::setAtom(JSAtom *atom)
 
 inline void
 Module::setScript(JSScript *script)
 {
     setReservedSlot(SCRIPT_SLOT, PrivateValue(script));
 }
 
 Module *
-Module::create(JSContext *cx, HandleAtom atom)
+Module::create(ExclusiveContext *cx, HandleAtom atom)
 {
     RootedObject object(cx, NewBuiltinClassInstance(cx, &class_));
     if (!object)
         return NULL;
     RootedModule module(cx, &object->as<Module>());
     module->setAtom(atom);
     module->setScript(NULL);
     return module;
--- a/js/src/builtin/Module.h
+++ b/js/src/builtin/Module.h
@@ -8,17 +8,17 @@
 #define builtin_Module_h
 
 #include "jsobj.h"
 
 namespace js {
 
 class Module : public JSObject {
   public:
-    static Module *create(JSContext *cx, js::HandleAtom atom);
+    static Module *create(ExclusiveContext *cx, js::HandleAtom atom);
 
     JSAtom *atom() {
         return &getReservedSlot(ATOM_SLOT).toString()->asAtom();
     };
 
     JSScript *script() {
         return (JSScript *) getReservedSlot(SCRIPT_SLOT).toPrivate();
     }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -10,16 +10,17 @@
 
 #include "jscntxt.h"
 #include "jsobj.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
+#include "jsstrinlines.h"
 
 using namespace js;
 using namespace js::types;
 
 using js::frontend::IsIdentifier;
 using mozilla::ArrayLength;
 
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -174,26 +174,29 @@ frontend::CompileScript(JSContext *cx, H
       case CompileOptions::NO_SOURCE:
         break;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
-        syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
+        syntaxParser.construct(cx, &cx->tempLifoAlloc(),
+                               options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) NULL,
                                (LazyScript *) NULL);
     }
 
-    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
+    Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
+                                    options, chars, length, /* foldConstants = */ true,
                                     canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = sct;
 
-    GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
+    GlobalSharedContext globalsc(cx, scopeChain,
+                                 options.strictOption, options.extraWarningsOption);
 
     bool savedCallerFun =
         options.compileAndGo &&
         evalCaller &&
         (evalCaller->function() || evalCaller->savedCallerFun);
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
                                                   options, staticLevel, sourceObject, 0, length));
     if (!script)
@@ -349,17 +352,17 @@ frontend::CompileLazyFunction(JSContext 
     options.setPrincipals(cx->compartment()->principals)
            .setOriginPrincipals(lazy->originPrincipals())
            .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
-    Parser<FullParseHandler> parser(cx, options, chars, length,
+    Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, NULL, lazy);
 
     uint32_t staticLevel = lazy->staticLevel(cx);
 
     ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
     if (!pn)
         return false;
 
@@ -417,24 +420,26 @@ frontend::CompileFunctionBody(JSContext 
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
-        syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
+        syntaxParser.construct(cx, &cx->tempLifoAlloc(),
+                               options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) NULL,
                                (LazyScript *) NULL);
     }
 
     JS_ASSERT(!options.forEval);
 
-    Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
+    Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
+                                    options, chars, length, /* foldConstants = */ true,
                                     canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = &sct;
 
     JS_ASSERT(fun);
     JS_ASSERT(fun->isTenured());
 
     fun->setArgCount(formals.length());
 
@@ -460,17 +465,17 @@ frontend::CompileFunctionBody(JSContext 
     if (!script)
         return false;
 
     // If the context is strict, immediately parse the body in strict
     // mode. Otherwise, we parse it normally. If we see a "use strict"
     // directive, we backup and reparse it as strict.
     TokenStream::Position start(parser.keepAtoms);
     parser.tokenStream.tell(&start);
-    bool strict = StrictModeFromContext(cx);
+    bool strict = options.strictOption;
     bool becameStrict;
     FunctionBox *funbox;
     ParseNode *pn;
     while (true) {
         pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
                                            strict, &becameStrict);
         if (pn)
             break;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -44,28 +44,28 @@ using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 using mozilla::DoubleIsInt32;
 using mozilla::PodCopy;
 
 static bool
-SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
+SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
 
 struct frontend::StmtInfoBCE : public StmtInfoBase
 {
     StmtInfoBCE     *down;          /* info for enclosing statement */
     StmtInfoBCE     *downScope;     /* next enclosing lexical scope */
 
     ptrdiff_t       update;         /* loop update offset (top if none) */
     ptrdiff_t       breaks;         /* offset of last break in loop */
     ptrdiff_t       continues;      /* offset of last continue in loop */
 
-    StmtInfoBCE(JSContext *cx) : StmtInfoBase(cx) {}
+    StmtInfoBCE(ExclusiveContext *cx) : StmtInfoBase(cx) {}
 
     /*
      * To reuse space, alias two of the ptrdiff_t fields for use during
      * try/catch/finally code generation and backpatching.
      *
      * Only a loop, switch, or label statement info record can have breaks and
      * continues, and only a for loop has an update backpatch chain, so it's
      * safe to overlay these for the "trying" StmtTypes.
@@ -117,17 +117,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
 
 bool
 BytecodeEmitter::init()
 {
     return atomIndices.ensureMap(sc->context);
 }
 
 static ptrdiff_t
-EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
+EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
 {
     ptrdiff_t offset = bce->code().length();
 
     // Start it off moderately large to avoid repeated resizings early on.
     if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
         return -1;
 
     jsbytecode dummy = 0;
@@ -142,17 +142,17 @@ static StaticBlockObject &
 CurrentBlock(StmtInfoBCE *topStmt)
 {
     JS_ASSERT(topStmt->type == STMT_BLOCK || topStmt->type == STMT_SWITCH);
     JS_ASSERT(topStmt->blockObj->is<StaticBlockObject>());
     return *topStmt->blockObj;
 }
 
 static void
-UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
+UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
 {
     jsbytecode *pc = bce->code(target);
     JSOp op = (JSOp) *pc;
     const JSCodeSpec *cs = &js_CodeSpec[op];
 
     if (cs->format & JOF_TMPSLOT_MASK) {
         /*
          * An opcode may temporarily consume stack space during execution.
@@ -185,44 +185,44 @@ UpdateDepth(JSContext *cx, BytecodeEmitt
     bce->stackDepth -= nuses;
     JS_ASSERT(bce->stackDepth >= 0);
     bce->stackDepth += ndefs;
     if ((unsigned)bce->stackDepth > bce->maxStackDepth)
         bce->maxStackDepth = bce->stackDepth;
 }
 
 ptrdiff_t
-frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
+frontend::Emit1(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 1);
     if (offset < 0)
         return -1;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
     UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
-frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
+frontend::Emit2(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 2);
     if (offset < 0)
         return -1;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
     code[1] = op1;
     UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
-frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
+frontend::Emit3(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
                     jsbytecode op2)
 {
     /* These should filter through EmitVarOp. */
     JS_ASSERT(!IsArgOp(op));
     JS_ASSERT(!IsLocalOp(op));
 
     ptrdiff_t offset = EmitCheck(cx, bce, 3);
     if (offset < 0)
@@ -232,17 +232,17 @@ frontend::Emit3(JSContext *cx, BytecodeE
     code[0] = jsbytecode(op);
     code[1] = op1;
     code[2] = op2;
     UpdateDepth(cx, bce, offset);
     return offset;
 }
 
 ptrdiff_t
-frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
+frontend::EmitN(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
 {
     ptrdiff_t length = 1 + (ptrdiff_t)extra;
     ptrdiff_t offset = EmitCheck(cx, bce, length);
     if (offset < 0)
         return -1;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
@@ -254,17 +254,17 @@ frontend::EmitN(JSContext *cx, BytecodeE
      */
     if (js_CodeSpec[op].nuses >= 0)
         UpdateDepth(cx, bce, offset);
 
     return offset;
 }
 
 static ptrdiff_t
-EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
+EmitJump(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
 {
     ptrdiff_t offset = EmitCheck(cx, bce, 5);
     if (offset < 0)
         return -1;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
     SET_JUMP_OFFSET(code, off);
@@ -301,41 +301,40 @@ static const char *
 StatementName(StmtInfoBCE *topStmt)
 {
     if (!topStmt)
         return js_script_str;
     return statementName[topStmt->type];
 }
 
 static void
-ReportStatementTooLarge(JSContext *cx, StmtInfoBCE *topStmt)
+ReportStatementTooLarge(TokenStream &ts, StmtInfoBCE *topStmt)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
-                         StatementName(topStmt));
+    ts.reportError(JSMSG_NEED_DIET, StatementName(topStmt));
 }
 
 /*
  * Emit a backpatch op with offset pointing to the previous jump of this type,
  * so that we can walk back up the chain fixing up the op and jump offset.
  */
 static ptrdiff_t
-EmitBackPatchOp(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t *lastp)
+EmitBackPatchOp(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t *lastp)
 {
     ptrdiff_t offset, delta;
 
     offset = bce->offset();
     delta = offset - *lastp;
     *lastp = offset;
     JS_ASSERT(delta > 0);
     return EmitJump(cx, bce, JSOP_BACKPATCH, delta);
 }
 
 /* Updates line number notes, not column notes. */
 static inline bool
-UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, uint32_t offset)
+UpdateLineNumberNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offset)
 {
     TokenStream *ts = &bce->parser->tokenStream;
     if (!ts->srcCoords.isOnThisLine(offset, bce->currentLine())) {
         unsigned line = ts->srcCoords.lineNum(offset);
         unsigned delta = line - bce->currentLine();
 
         /*
          * Encode any change in the current source line number by using
@@ -360,17 +359,17 @@ UpdateLineNumberNotes(JSContext *cx, Byt
             } while (--delta != 0);
         }
     }
     return true;
 }
 
 /* A function, so that we avoid macro-bloating all the other callsites. */
 static bool
-UpdateSourceCoordNotes(JSContext *cx, BytecodeEmitter *bce, uint32_t offset)
+UpdateSourceCoordNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t offset)
 {
     if (!UpdateLineNumberNotes(cx, bce, offset))
         return false;
 
     uint32_t columnIndex = bce->parser->tokenStream.srcCoords.columnIndex(offset);
     ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(bce->current->lastColumn);
     if (colspan != 0) {
         if (colspan < 0) {
@@ -386,17 +385,17 @@ UpdateSourceCoordNotes(JSContext *cx, By
         if (NewSrcNote2(cx, bce, SRC_COLSPAN, colspan) < 0)
             return false;
         bce->current->lastColumn = columnIndex;
     }
     return true;
 }
 
 static ptrdiff_t
-EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+EmitLoopHead(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
 {
     if (nextpn) {
         /*
          * Try to give the JSOP_LOOPHEAD the same line number as the next
          * instruction. nextpn is often a block, in which case the next
          * instruction typically comes from the first statement inside.
          */
         JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
@@ -405,17 +404,17 @@ EmitLoopHead(JSContext *cx, BytecodeEmit
         if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
             return -1;
     }
 
     return Emit1(cx, bce, JSOP_LOOPHEAD);
 }
 
 static bool
-EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+EmitLoopEntry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
 {
     if (nextpn) {
         /* Update the line number, as for LOOPHEAD. */
         JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
         if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
             nextpn = nextpn->pn_head;
         if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
             return false;
@@ -440,17 +439,17 @@ EmitLoopEntry(JSContext *cx, BytecodeEmi
     return Emit2(cx, bce, JSOP_LOOPENTRY, uint8_t(loopDepth)) >= 0;
 }
 
 /*
  * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
  * a type set to store its result.
  */
 static inline void
-CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
+CheckTypeSet(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (js_CodeSpec[op].format & JOF_TYPESET) {
         if (bce->typesetCount < UINT16_MAX)
             bce->typesetCount++;
     }
 }
 
 /*
@@ -489,41 +488,41 @@ CheckTypeSet(JSContext *cx, BytecodeEmit
         bce->code(offset)[0] = op;                                            \
         bce->code(offset)[1] = jsbytecode(i >> 24);                           \
         bce->code(offset)[2] = jsbytecode(i >> 16);                           \
         bce->code(offset)[3] = jsbytecode(i >> 8);                            \
         bce->code(offset)[4] = jsbytecode(i);                                 \
     JS_END_MACRO
 
 static bool
-FlushPops(JSContext *cx, BytecodeEmitter *bce, int *npops)
+FlushPops(ExclusiveContext *cx, BytecodeEmitter *bce, int *npops)
 {
     JS_ASSERT(*npops != 0);
     if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
         return false;
     EMIT_UINT16_IMM_OP(JSOP_POPN, *npops);
     *npops = 0;
     return true;
 }
 
 static bool
-PopIterator(JSContext *cx, BytecodeEmitter *bce)
+PopIterator(ExclusiveContext *cx, BytecodeEmitter *bce)
 {
     if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
         return false;
     if (Emit1(cx, bce, JSOP_ENDITER) < 0)
         return false;
     return true;
 }
 
 /*
  * Emit additional bytecode(s) for non-local jumps.
  */
 static bool
-EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfoBCE *toStmt)
+EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *toStmt)
 {
     /*
      * The non-local jump fixup we emit will unbalance bce->stackDepth, because
      * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
      * end of a with statement, so we save bce->stackDepth here and restore it
      * just before a successful return.
      */
     int depth = bce->stackDepth;
@@ -603,32 +602,32 @@ EmitNonLocalJumpFixup(JSContext *cx, Byt
     return true;
 
 #undef FLUSH_POPS
 }
 
 static const jsatomid INVALID_ATOMID = -1;
 
 static ptrdiff_t
-EmitGoto(JSContext *cx, BytecodeEmitter *bce, StmtInfoBCE *toStmt, ptrdiff_t *lastp,
+EmitGoto(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *toStmt, ptrdiff_t *lastp,
          SrcNoteType noteType = SRC_NULL)
 {
     if (!EmitNonLocalJumpFixup(cx, bce, toStmt))
         return -1;
 
     if (noteType != SRC_NULL) {
         if (NewSrcNote(cx, bce, noteType) < 0)
             return -1;
     }
 
     return EmitBackPatchOp(cx, bce, lastp);
 }
 
 static bool
-BackPatch(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *target, jsbytecode op)
+BackPatch(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *target, jsbytecode op)
 {
     jsbytecode *pc, *stop;
     ptrdiff_t delta, span;
 
     pc = bce->code(last);
     stop = bce->code(-1);
     while (pc != stop) {
         delta = GET_JUMP_OFFSET(pc);
@@ -676,120 +675,120 @@ PushBlockScopeBCE(BytecodeEmitter *bce, 
     PushStatementBCE(bce, stmt, STMT_BLOCK, top);
     blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
     FinishPushBlockScope(bce, stmt, blockObj);
 }
 
 // Patches |breaks| and |continues| unless the top statement info record
 // represents a try-catch-finally suite. May fail if a jump offset overflows.
 static bool
-PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
+PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
 {
     StmtInfoBCE *stmt = bce->topStmt;
     if (!stmt->isTrying() &&
         (!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) ||
          !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
     {
         return false;
     }
     FinishPopStatement(bce);
     return true;
 }
 
 static bool
-EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
+EmitIndex32(ExclusiveContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
     JS_ASSERT(len == size_t(js_CodeSpec[op].length));
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
     SET_UINT32_INDEX(code, index);
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static bool
-EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
+EmitIndexOp(ExclusiveContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
 {
     const size_t len = js_CodeSpec[op].length;
     JS_ASSERT(len >= 1 + UINT32_INDEX_LEN);
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
     jsbytecode *code = bce->code(offset);
     code[0] = jsbytecode(op);
     SET_UINT32_INDEX(code, index);
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static bool
-EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
+EmitAtomOp(ExclusiveContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
     if (op == JSOP_GETPROP && atom == cx->names().length) {
         /* Specialize length accesses for the interpreter. */
         op = JSOP_LENGTH;
     }
 
     jsatomid index;
     if (!bce->makeAtomIndex(atom, &index))
         return false;
 
     return EmitIndexOp(cx, op, index, bce);
 }
 
 static bool
-EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitAtomOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->pn_atom != NULL);
     return EmitAtomOp(cx, pn->pn_atom, op, bce);
 }
 
 static bool
-EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
+EmitObjectOp(ExclusiveContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
     return EmitIndex32(cx, op, bce->objectList.add(objbox), bce);
 }
 
 static bool
-EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce)
+EmitRegExp(ExclusiveContext *cx, uint32_t index, BytecodeEmitter *bce)
 {
     return EmitIndex32(cx, JSOP_REGEXP, index, bce);
 }
 
 /*
  * To catch accidental misuse, EMIT_UINT16_IMM_OP/Emit3 assert that they are
  * not used to unconditionally emit JSOP_GETLOCAL. Variable access should
  * instead be emitted using EmitVarOp. In special cases, when the caller
  * definitely knows that a given local slot is unaliased, this function may be
  * used as a non-asserting version of EMIT_UINT16_IMM_OP.
  */
 static bool
-EmitUnaliasedVarOp(JSContext *cx, JSOp op, uint16_t slot, BytecodeEmitter *bce)
+EmitUnaliasedVarOp(ExclusiveContext *cx, JSOp op, uint16_t slot, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) != JOF_SCOPECOORD);
     ptrdiff_t off = EmitN(cx, bce, op, sizeof(uint16_t));
     if (off < 0)
         return false;
     SET_UINT16(bce->code(off), slot);
     return true;
 }
 
 static bool
-EmitAliasedVarOp(JSContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmitter *bce)
+EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_SCOPECOORD);
 
     uint32_t maybeBlockIndex = UINT32_MAX;
     if (bce->blockChain)
         maybeBlockIndex = bce->objectList.indexOf(bce->blockChain);
 
     unsigned n = 2 * sizeof(uint16_t) + sizeof(uint32_t);
@@ -837,17 +836,17 @@ LookupAliasedName(HandleScript script, P
             }
             slot++;
         }
     }
     return false;
 }
 
 static bool
-EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
+EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
 {
     unsigned skippedScopes = 0;
     BytecodeEmitter *bceOfDef = bce;
     if (pn->isUsed()) {
         /*
          * As explained in BindNameToSlot, the 'level' of a use indicates how
          * many function scopes (i.e., BytecodeEmitters) to skip to find the
          * enclosing function scope of the definition being accessed.
@@ -889,17 +888,17 @@ EmitAliasedVarOp(JSContext *cx, JSOp op,
             sc.slot = b->localIndexToSlot(bceOfDef->script->bindings, local);
         }
     }
 
     return EmitAliasedVarOp(cx, op, sc, bce);
 }
 
 static bool
-EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitVarOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
     JS_ASSERT(!pn->pn_cookie.isFree());
 
     if (IsAliasedVarOp(op)) {
         ScopeCoordinate sc;
         sc.hops = pn->pn_cookie.level();
         sc.slot = pn->pn_cookie.slot();
@@ -930,17 +929,17 @@ GetIncDecInfo(ParseNodeKind kind, bool *
 {
     JS_ASSERT(kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT ||
               kind == PNK_POSTDECREMENT || kind == PNK_PREDECREMENT);
     *post = kind == PNK_POSTINCREMENT || kind == PNK_POSTDECREMENT;
     return (kind == PNK_POSTINCREMENT || kind == PNK_PREINCREMENT) ? JSOP_ADD : JSOP_SUB;
 }
 
 static bool
-EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
+EmitVarIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
 {
     JSOp op = pn->pn_kid->getOp();
     JS_ASSERT(IsArgOp(op) || IsLocalOp(op) || IsAliasedVarOp(op));
     JS_ASSERT(pn->pn_kid->isKind(PNK_NAME));
     JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
 
     bool post;
     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
@@ -1022,31 +1021,31 @@ BytecodeEmitter::isAliasedName(ParseNode
  * Adjust the slot for a block local to account for the number of variables
  * that share the same index space with locals. Due to the incremental code
  * generation for top-level script, we do the adjustment via code patching in
  * js::frontend::CompileScript; see comments there.
  *
  * The function returns -1 on failures.
  */
 static int
-AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
+AdjustBlockSlot(ExclusiveContext *cx, BytecodeEmitter *bce, int slot)
 {
     JS_ASSERT((unsigned) slot < bce->maxStackDepth);
     if (bce->sc->isFunctionBox()) {
         slot += bce->script->bindings.numVars();
         if ((unsigned) slot >= SLOTNO_LIMIT) {
             bce->reportError(NULL, JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
     return slot;
 }
 
 static bool
-EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
+EmitEnterBlock(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
         return false;
 
     Rooted<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->as<StaticBlockObject>());
 
     int depth = bce->stackDepth -
@@ -1065,17 +1064,17 @@ EmitEnterBlock(JSContext *cx, BytecodeEm
         /* Beware the empty destructuring dummy. */
         if (!dn) {
             blockObj->setAliased(i, bce->sc->bindingsAccessedDynamically());
             continue;
         }
 
         JS_ASSERT(dn->isDefn());
         JS_ASSERT(unsigned(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
-        if (!dn->pn_cookie.set(cx, dn->pn_cookie.level(),
+        if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(),
                                uint16_t(dn->frameSlot() + depthPlusFixed)))
             return false;
 
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
@@ -1163,17 +1162,17 @@ TryConvertFreeName(BytecodeEmitter *bce,
                 if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
                     JSOp op;
                     switch (pn->getOp()) {
                       case JSOP_NAME:     op = JSOP_GETALIASEDVAR; break;
                       case JSOP_SETNAME:  op = JSOP_SETALIASEDVAR; break;
                       default: return false;
                     }
                     pn->setOp(op);
-                    JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->sc->context, hops, slot));
+                    JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->parser->tokenStream, hops, slot));
                     return true;
                 }
                 hops++;
             }
 
             if (script->funHasExtensibleScope || script->directlyInsideEval)
                 return false;
         }
@@ -1236,17 +1235,17 @@ TryConvertFreeName(BytecodeEmitter *bce,
  * name, PND_CONST will be set in pn_dflags for read-only properties after a
  * successful return.
  *
  * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
  * to update the special cases in EmitFor (for-in) and EmitAssignment (= and
  * op=, e.g. +=).
  */
 static bool
-BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+BindNameToSlotHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_NAME));
 
     JS_ASSERT_IF(pn->isKind(PNK_FUNCTION), pn->isBound());
 
     /* Don't attempt if 'pn' is already bound or deoptimized or a function. */
     if (pn->isBound() || pn->isDeoptimized())
         return true;
@@ -1284,17 +1283,17 @@ BindNameToSlotHelper(JSContext *cx, Byte
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
         break;
       default:
         if (pn->isConst()) {
             if (bce->sc->needStrictChecks()) {
                 JSAutoByteString name;
-                if (!js_AtomToPrintableString(cx, pn->pn_atom, &name) ||
+                if (!AtomToPrintableString(cx, pn->pn_atom, &name) ||
                     !bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
                 {
                     return false;
                 }
             }
             pn->setOp(op = JSOP_NAME);
         }
     }
@@ -1457,30 +1456,30 @@ BindNameToSlotHelper(JSContext *cx, Byte
         for (unsigned i = 0; i < skip; i++)
             bceSkipped = bceSkipped->parent;
         if (!bceSkipped->sc->isFunctionBox())
             return true;
     }
 
     JS_ASSERT(!pn->isOp(op));
     pn->setOp(op);
-    if (!pn->pn_cookie.set(bce->sc->context, skip, dn->pn_cookie.slot()))
+    if (!pn->pn_cookie.set(bce->parser->tokenStream, skip, dn->pn_cookie.slot()))
         return false;
 
     pn->pn_dflags |= PND_BOUND;
     return true;
 }
 
 /*
  * Attempts to bind the name, then checks that no dynamic scope lookup ops are
  * emitted in self-hosting mode. NAME ops do lookups off current scope chain,
  * and we do not want to allow self-hosted code to use the dynamic scope.
  */
 static bool
-BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+BindNameToSlot(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!BindNameToSlotHelper(cx, bce, pn))
         return false;
 
     if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
         bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
         return false;
     }
@@ -1496,17 +1495,17 @@ BindNameToSlot(JSContext *cx, BytecodeEm
  * The caller should initialize *answer to false and invoke this function on
  * an expression statement or similar subtree to decide whether the tree could
  * produce code that has any side effects.  For an expression statement, we
  * define useless code as code with no side effects, because the main effect,
  * the value left on the stack after the code executes, will be discarded by a
  * pop bytecode.
  */
 static bool
-CheckSideEffects(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *answer)
+CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool *answer)
 {
     if (!pn || *answer)
         return true;
 
     switch (pn->getArity()) {
       case PN_CODE:
         /*
          * A named function, contrary to ES3, is no longer useful, because we
@@ -1724,25 +1723,30 @@ BytecodeEmitter::needsImplicitThis()
     for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
         if (stmt->type == STMT_WITH)
             return true;
     }
     return false;
 }
 
 void
-BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext *cx)
+BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext *cx)
 {
+    // Note: when parsing off thread the resulting scripts need to be handed to
+    // the debugger after rejoining to the main thread.
+    if (!cx->isJSContext())
+        return;
+
     RootedFunction function(cx, script->function());
-    CallNewScriptHook(cx, script, function);
+    CallNewScriptHook(cx->asJSContext(), script, function);
     if (!parent) {
         GlobalObject *compileAndGoGlobal = NULL;
         if (script->compileAndGo)
             compileAndGoGlobal = &script->global();
-        Debugger::onNewScript(cx, script, compileAndGoGlobal);
+        Debugger::onNewScript(cx->asJSContext(), script, compileAndGoGlobal);
     }
 }
 
 inline TokenStream *
 BytecodeEmitter::tokenStream()
 {
     return &parser->tokenStream;
 }
@@ -1781,17 +1785,17 @@ BytecodeEmitter::reportStrictModeError(P
     va_start(args, errorNumber);
     bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict,
                                                                errorNumber, args);
     va_end(args);
     return result;
 }
 
 static bool
-EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
+EmitNameOp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
 {
     JSOp op;
 
     if (!BindNameToSlot(cx, bce, pn))
         return false;
     op = pn->getOp();
 
     if (callContext) {
@@ -1846,33 +1850,33 @@ EmitNameOp(JSContext *cx, BytecodeEmitte
         if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
             return false;
     }
 
     return true;
 }
 
 static inline bool
-EmitElemOpBase(JSContext *cx, BytecodeEmitter *bce, JSOp op)
+EmitElemOpBase(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (Emit1(cx, bce, op) < 0)
         return false;
     CheckTypeSet(cx, bce, op);
 
     if (op == JSOP_CALLELEM) {
         if (Emit1(cx, bce, JSOP_SWAP) < 0)
             return false;
         if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
             return false;
     }
     return true;
 }
 
 static bool
-EmitPropLHS(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitPropLHS(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isKind(PNK_DOT));
     ParseNode *pn2 = pn->maybeExpr();
 
     /*
      * If the object operand is also a dotted property reference, reverse the
      * list linked via pn_expr temporarily so we can iterate over it from the
      * bottom up (reversing again as we go), to avoid excessive recursion.
@@ -1910,17 +1914,17 @@ EmitPropLHS(JSContext *cx, ParseNode *pn
         return true;
     }
 
     // The non-optimized case.
     return EmitTree(cx, bce, pn2);
 }
 
 static bool
-EmitPropOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitPropOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isArity(PN_NAME));
 
     if (!EmitPropLHS(cx, pn, op, bce))
         return false;
 
     if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_DUP) < 0)
         return false;
@@ -1933,17 +1937,17 @@ EmitPropOp(JSContext *cx, ParseNode *pn,
 
     if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_NOTEARG) < 0)
         return false;
 
     return true;
 }
 
 static bool
-EmitPropIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
+EmitPropIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->pn_kid->getKind() == PNK_DOT);
 
     bool post;
     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
 
     JSOp get = JSOP_GETPROP;
     if (!EmitPropLHS(cx, pn->pn_kid, get, bce))     // OBJ
@@ -1972,17 +1976,17 @@ EmitPropIncDec(JSContext *cx, ParseNode 
         return false;
     if (post && Emit1(cx, bce, JSOP_POP) < 0)       // RESULT
         return false;
 
     return true;
 }
 
 static bool
-EmitNameIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
+EmitNameIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
 {
     const JSCodeSpec *cs = &js_CodeSpec[pn->pn_kid->getOp()];
 
     bool global = (cs->format & JOF_GNAME);
     bool post;
     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
 
     if (!EmitAtomOp(cx, pn->pn_kid, global ? JSOP_BINDGNAME : JSOP_BINDNAME, bce))  // OBJ
@@ -2009,17 +2013,17 @@ EmitNameIncDec(JSContext *cx, ParseNode 
         return false;
     if (post && Emit1(cx, bce, JSOP_POP) < 0)       // RESULT
         return false;
 
     return true;
 }
 
 static bool
-EmitElemOperands(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitElemOperands(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     ParseNode *left, *right;
 
     if (pn->isArity(PN_NAME)) {
         /*
          * Set left and right so pn appears to be a PNK_ELEM node, instead of
          * a PNK_DOT node. See the PNK_FOR/IN case in EmitTree, and
          * EmitDestructuringOps nearer below. In the destructuring case, the
@@ -2056,23 +2060,23 @@ EmitElemOperands(JSContext *cx, ParseNod
 
     if (!EmitTree(cx, bce, right))
         return false;
 
     return true;
 }
 
 static bool
-EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
+EmitElemOp(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     return EmitElemOperands(cx, pn, op, bce) && EmitElemOpBase(cx, bce, op);
 }
 
 static bool
-EmitElemIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
+EmitElemIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->pn_kid->getKind() == PNK_ELEM);
 
     if (!EmitElemOperands(cx, pn->pn_kid, JSOP_GETELEM, bce))
         return false;
 
     bool post;
     JSOp binop = GetIncDecInfo(pn->getKind(), &post);
@@ -2110,17 +2114,17 @@ EmitElemIncDec(JSContext *cx, ParseNode 
         return false;
     if (post && Emit1(cx, bce, JSOP_POP) < 0)       // RESULT
         return false;
 
     return true;
 }
 
 static bool
-EmitNumberOp(JSContext *cx, double dval, BytecodeEmitter *bce)
+EmitNumberOp(ExclusiveContext *cx, double dval, BytecodeEmitter *bce)
 {
     int32_t ival;
     uint32_t u;
     ptrdiff_t off;
     jsbytecode *pc;
 
     if (DoubleIsInt32(dval, &ival)) {
         if (ival == 0)
@@ -2162,17 +2166,17 @@ SetJumpOffsetAt(BytecodeEmitter *bce, pt
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047.
  * LLVM is deciding to inline this function which uses a lot of stack space
  * into EmitTree which is recursive and uses relatively little stack space.
  */
 MOZ_NEVER_INLINE static bool
-EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JSOp switchOp;
     bool hasDefault;
     ptrdiff_t top, off, defaultOffset;
     ParseNode *pn2, *pn3, *pn4;
     int32_t low, high;
     int noteIndex;
     size_t switchSize;
@@ -2232,18 +2236,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
     }
 #endif
 
     uint32_t caseCount = pn2->pn_count;
     uint32_t tableLength = 0;
     ScopedJSFreePtr<ParseNode*> table(NULL);
 
     if (caseCount > JS_BIT(16)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_TOO_MANY_CASES);
+        bce->parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
         return false;
     }
 
     if (caseCount == 0 ||
         (caseCount == 1 &&
          (hasDefault = (pn2->pn_head->isKind(PNK_DEFAULT))))) {
         caseCount = 0;
         low = 0;
@@ -2305,17 +2308,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
                     i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
                     intmap = intmap_space;
                     intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2;
                 } else {
                     /* Just grab 8K for the worst-case bitmap. */
                     intmap_bitlen = JS_BIT(16);
                     intmap = cx->pod_malloc<jsbitmap>(JS_BIT(16) >> JS_BITS_PER_WORD_LOG2);
                     if (!intmap) {
-                        JS_ReportOutOfMemory(cx);
+                        js_ReportOutOfMemory(cx);
                         return false;
                     }
                 }
                 memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
             }
             if (JS_TEST_BIT(intmap, i)) {
                 switchOp = JSOP_CONDSWITCH;
                 continue;
@@ -2511,17 +2514,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
     if (pn->pn_right->isKind(PNK_LEXICALSCOPE))
         EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
 #endif
 
     return true;
 }
 
 bool
-frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body)
+frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body)
 {
     /*
      * The decompiler has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
@@ -2599,17 +2602,17 @@ frontend::EmitFunctionScript(JSContext *
     fun->setScript(bce->script);
 
     bce->tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 static bool
-MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
+MaybeEmitVarDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
                  jsatomid *result)
 {
     jsatomid atomIndex;
 
     if (!pn->pn_cookie.isFree()) {
         atomIndex = pn->pn_cookie.slot();
     } else {
         if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
@@ -2645,31 +2648,31 @@ enum VarEmitOption
     DefineVars        = 0,
     PushInitialValues = 1,
     InitializeVars    = 2
 };
 
 #if JS_HAS_DESTRUCTURING
 
 typedef bool
-(*DestructuringDeclEmitter)(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
+(*DestructuringDeclEmitter)(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
 
 static bool
-EmitDestructuringDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
+EmitDestructuringDecl(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_NAME));
     if (!BindNameToSlot(cx, bce, pn))
         return false;
 
     JS_ASSERT(!pn->isOp(JSOP_CALLEE));
     return MaybeEmitVarDecl(cx, bce, prologOp, pn, NULL);
 }
 
 static bool
-EmitDestructuringDecls(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
+EmitDestructuringDecls(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
 {
     ParseNode *pn2, *pn3;
     DestructuringDeclEmitter emitter;
 
     if (pn->isKind(PNK_ARRAY)) {
         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
             if (pn2->isKind(PNK_ELISION))
                 continue;
@@ -2687,33 +2690,33 @@ EmitDestructuringDecls(JSContext *cx, By
             if (!emitter(cx, bce, prologOp, pn3))
                 return false;
         }
     }
     return true;
 }
 
 static bool
-EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
+EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
                            VarEmitOption emitOption);
 
 /*
  * EmitDestructuringLHS assumes the to-be-destructured value has been pushed on
  * the stack and emits code to destructure a single lhs expression (either a
  * name or a compound []/{} expression).
  *
  * If emitOption is InitializeVars, the to-be-destructured value is assigned to
  * locals and ultimately the initial slot is popped (-1 total depth change).
  *
  * If emitOption is PushInitialValues, the to-be-destructured value is replaced
  * with the initial values of the N (where 0 <= N) variables assigned in the
  * lhs expression. (Same post-condition as EmitDestructuringOpsHelper)
  */
 static bool
-EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
+EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
 {
     JS_ASSERT(emitOption != DefineVars);
 
     /*
      * Now emit the lvalue opcode sequence.  If the lvalue is a nested
      * destructuring initialiser-form, call ourselves to handle it, then
      * pop the matched value.  Otherwise emit an lvalue bytecode sequence
      * ending with a JSOP_ENUMELEM or equivalent op.
@@ -2820,17 +2823,17 @@ EmitDestructuringLHS(JSContext *cx, Byte
  * If emitOption is InitializeVars, the initial to-be-destructured value is
  * left untouched on the stack and the overall depth is not changed.
  *
  * If emitOption is PushInitialValues, the to-be-destructured value is replaced
  * with the initial values of the N (where 0 <= N) variables assigned in the
  * lhs expression. (Same post-condition as EmitDestructuringLHS)
  */
 static bool
-EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
+EmitDestructuringOpsHelper(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
                            VarEmitOption emitOption)
 {
     JS_ASSERT(emitOption != DefineVars);
 
     unsigned index;
     ParseNode *pn2, *pn3;
     bool doElemOp;
 
@@ -2937,28 +2940,28 @@ EmitDestructuringOpsHelper(JSContext *cx
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitDestructuringOps(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool isLet = false)
+EmitDestructuringOps(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool isLet = false)
 {
     /*
      * Call our recursive helper to emit the destructuring assignments and
      * related stack manipulations.
      */
     VarEmitOption emitOption = isLet ? PushInitialValues : InitializeVars;
     return EmitDestructuringOpsHelper(cx, bce, pn, emitOption);
 }
 
 static bool
-EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp,
+EmitGroupAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp,
                     ParseNode *lhs, ParseNode *rhs)
 {
     unsigned depth, limit, i, nslots;
     ParseNode *pn;
 
     depth = limit = (unsigned) bce->stackDepth;
     for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
         if (limit == JS_BIT(16)) {
@@ -3002,17 +3005,17 @@ EmitGroupAssignment(JSContext *cx, Bytec
 enum GroupOption { GroupIsDecl, GroupIsNotDecl };
 
 /*
  * Helper called with pop out param initialized to a JSOP_POP* opcode.  If we
  * can emit a group assignment sequence, which results in 0 stack depth delta,
  * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
  */
 static bool
-MaybeEmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
+MaybeEmitGroupAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
                          GroupOption groupOption, JSOp *pop)
 {
     JS_ASSERT(pn->isKind(PNK_ASSIGN));
     JS_ASSERT(pn->isOp(JSOP_NOP));
     JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
 
     ParseNode *lhs = pn->pn_left;
     ParseNode *rhs = pn->pn_right;
@@ -3035,17 +3038,17 @@ MaybeEmitGroupAssignment(JSContext *cx, 
  * Instead of issuing a sequence |dup|eval-rhs|set-lhs|pop| (which doesn't work
  * since the bound vars don't yet have slots), just eval/push each rhs element
  * just like what EmitLet would do for 'let (x = a, y = b) ...'. While shorter,
  * simpler and more efficient than MaybeEmitGroupAssignment, it is harder to
  * decompile so we restrict the ourselves to cases where the lhs and rhs are in
  * 1:1 correspondence and lhs elements are simple names.
  */
 static bool
-MaybeEmitLetGroupDecl(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp *pop)
+MaybeEmitLetGroupDecl(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp *pop)
 {
     JS_ASSERT(pn->isKind(PNK_ASSIGN));
     JS_ASSERT(pn->isOp(JSOP_NOP));
     JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
 
     ParseNode *lhs = pn->pn_left;
     ParseNode *rhs = pn->pn_right;
     if (lhs->isKind(PNK_ARRAY) && rhs->isKind(PNK_ARRAY) &&
@@ -3066,17 +3069,17 @@ MaybeEmitLetGroupDecl(JSContext *cx, Byt
         *pop = JSOP_NOP;
     }
     return true;
 }
 
 #endif /* JS_HAS_DESTRUCTURING */
 
 static bool
-EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
+EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
               bool isLet = false)
 {
     JS_ASSERT(pn->isArity(PN_LIST));
     JS_ASSERT(isLet == (emitOption == PushInitialValues));
 
     ParseNode *next;
     for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) {
         next = pn2->pn_next;
@@ -3255,17 +3258,17 @@ EmitVariables(JSContext *cx, BytecodeEmi
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, ParseNode *rhs)
+EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, ParseNode *rhs)
 {
     /*
      * Check left operand type and generate specialized code for it.
      * Specialize to avoid ECMA "reference type" values on the operand
      * stack, which impose pervasive runtime "GetValue" costs.
      */
     jsatomid atomIndex = (jsatomid) -1;
     jsbytecode offset = 1;
@@ -3460,17 +3463,17 @@ EmitAssignment(JSContext *cx, BytecodeEm
 #endif
       default:
         JS_ASSERT(0);
     }
     return true;
 }
 
 static bool
-EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
+EmitNewInit(ExclusiveContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
     ptrdiff_t offset = EmitCheck(cx, bce, len);
     if (offset < 0)
         return false;
 
     jsbytecode *code = bce->code(offset);
     code[0] = JSOP_NEWINIT;
@@ -3479,17 +3482,17 @@ EmitNewInit(JSContext *cx, BytecodeEmitt
     code[3] = 0;
     code[4] = 0;
     UpdateDepth(cx, bce, offset);
     CheckTypeSet(cx, bce, JSOP_NEWINIT);
     return true;
 }
 
 bool
-ParseNode::getConstantValue(JSContext *cx, bool strictChecks, MutableHandleValue vp)
+ParseNode::getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHandleValue vp)
 {
     switch (getKind()) {
       case PNK_NUMBER:
         vp.setNumber(pn_dval);
         return true;
       case PNK_STRING:
         vp.setString(pn_atom);
         return true;
@@ -3582,17 +3585,17 @@ ParseNode::getConstantValue(JSContext *c
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected node");
     }
     return false;
 }
 
 static bool
-EmitSingletonInitialiser(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitSingletonInitialiser(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, bce->sc->needStrictChecks(), &value))
         return false;
 
     JS_ASSERT(value.isObject());
     ObjectBox *objbox = bce->parser->newObjectBox(&value.toObject());
     if (!objbox)
@@ -3609,17 +3612,17 @@ class EmitLevelManager
 {
     BytecodeEmitter *bce;
   public:
     EmitLevelManager(BytecodeEmitter *bce) : bce(bce) { bce->emitLevel++; }
     ~EmitLevelManager() { bce->emitLevel--; }
 };
 
 static bool
-EmitCatch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitCatch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     ptrdiff_t guardJump;
 
     /*
      * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
      * and save the block object atom.
      */
     StmtInfoBCE *stmt = bce->topStmt;
@@ -3693,17 +3696,17 @@ EmitCatch(JSContext *cx, BytecodeEmitter
     return NewSrcNote(cx, bce, SRC_CATCH) >= 0;
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitTry(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     StmtInfoBCE stmtInfo(cx);
     ptrdiff_t catchJump = -1;
 
     /*
      * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
      * for later fixup.
      *
@@ -3914,17 +3917,17 @@ EmitTry(JSContext *cx, BytecodeEmitter *
      */
     if (pn->pn_kid3 && !bce->tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart))
         return false;
 
     return true;
 }
 
 static bool
-EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitIf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     StmtInfoBCE stmtInfo(cx);
 
     /* Initialize so we can detect else-if chains and avoid recursion. */
     stmtInfo.type = STMT_IF;
     ptrdiff_t beq = -1;
     ptrdiff_t jmp = -1;
     ptrdiff_t noteIndex = -1;
@@ -4028,17 +4031,17 @@ EmitIf(JSContext *cx, BytecodeEmitter *b
  * otherwise touch the stack, evaluation of the let-var initializers must leave
  * the initial value in the let-var's future slot.
  */
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
+EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
 {
     JS_ASSERT(pnLet->isArity(PN_BINARY));
     ParseNode *varList = pnLet->pn_left;
     JS_ASSERT(varList->isArity(PN_LIST));
     ParseNode *letBody = pnLet->pn_right;
     JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
     Rooted<StaticBlockObject*> blockObj(cx, &letBody->pn_objbox->object->as<StaticBlockObject>());
 
@@ -4076,17 +4079,17 @@ EmitLet(JSContext *cx, BytecodeEmitter *
 }
 #endif
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
 
     StmtInfoBCE stmtInfo(cx);
     ObjectBox *objbox = pn->pn_objbox;
     StaticBlockObject &blockObj = objbox->object->as<StaticBlockObject>();
     size_t slots = blockObj.slotCount();
@@ -4099,34 +4102,34 @@ EmitLexicalScope(JSContext *cx, Bytecode
         return false;
 
     EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, slots);
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     StmtInfoBCE stmtInfo(cx);
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
     PushStatementBCE(bce, &stmtInfo, STMT_WITH, bce->offset());
     if (Emit1(cx, bce, JSOP_ENTERWITH) < 0)
         return false;
 
     if (!EmitTree(cx, bce, pn->pn_right))
         return false;
     if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
         return false;
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
     ParseNode *pn1 = forHead->pn_kid1;
@@ -4281,17 +4284,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
 
     if (letDecl)
         EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
 
     return true;
 }
 
 static bool
-EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_FOR_LOOP, top);
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
     /* C-style for (init; cond; update) ... loop. */
@@ -4432,26 +4435,26 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
     if (!bce->tryNoteList.append(JSTRY_LOOP, bce->stackDepth, top, bce->offset()))
         return false;
 
     /* Now fixup all breaks and continues. */
     return PopStatementBCE(cx, bce);
 }
 
 static inline bool
-EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
     return pn->pn_left->isKind(PNK_FORIN)
            ? EmitForIn(cx, bce, pn, top)
            : EmitNormalFor(cx, bce, pn, top);
 }
 
 static JS_NEVER_INLINE bool
-EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     FunctionBox *funbox = pn->pn_funbox;
     RootedFunction fun(cx, funbox->function());
     if (fun->isNative()) {
         JS_ASSERT(IsAsmJSModuleNative(fun->native()));
         return true;
     }
     JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
@@ -4472,17 +4475,18 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
 
     /*
      * Mark as singletons any function which will only be executed once, or
      * which is inner to a lambda we only expect to run once. In the latter
      * case, if the lambda runs multiple times then CloneFunctionObject will
      * make a deep clone of its contents.
      */
     bool singleton =
-        cx->typeInferenceEnabled() &&
+        cx->isJSContext() &&
+        cx->asJSContext()->typeInferenceEnabled() &&
         bce->script->compileAndGo &&
         (bce->checkSingletonContext() ||
          (!bce->isInLoop() &&
           bce->parent &&
           bce->parent->emittingRunOnceLambda));
     if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
         return false;
 
@@ -4497,38 +4501,44 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
         SharedContext *outersc = bce->sc;
 
         if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
             funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
         JS_ASSERT_IF(outersc->strict, funbox->strict);
 
         // Inherit most things (principals, version, etc) from the parent.
         Rooted<JSScript*> parent(cx, bce->script);
-        CompileOptions options(cx);
+        CompileOptions options(bce->parser->options());
         options.setPrincipals(parent->principals())
                .setOriginPrincipals(parent->originPrincipals)
                .setCompileAndGo(parent->compileAndGo)
                .setSelfHostingMode(parent->selfHosted)
                .setNoScriptRval(false)
+               .setForEval(false)
                .setVersion(parent->getVersion());
 
         bool generateBytecode = true;
 #ifdef JS_ION
         if (funbox->useAsm) {
+            if (!cx->isJSContext()) {
+                bce->parser->tokenStream.reportError(JSMSG_SYNTAX_ERROR);
+                return false;
+            }
+
             RootedFunction moduleFun(cx);
 
             // In a function like this:
             //
             //   function f() { "use asm"; ... }
             //
             // funbox->asmStart points to the '"', and funbox->bufEnd points
             // one past the final '}'.  We need to exclude that final '}',
             // so we use |funbox->bufEnd - 1| below.
             //
-            if (!CompileAsmJS(cx, *bce->tokenStream(), pn, options,
+            if (!CompileAsmJS(cx->asJSContext(), *bce->tokenStream(), pn, options,
                               bce->script->scriptSource(), funbox->asmStart, funbox->bufEnd - 1,
                               &moduleFun))
                 return false;
 
             if (moduleFun) {
                 funbox->object = moduleFun;
                 generateBytecode = false;
             }
@@ -4607,17 +4617,17 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitDo(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitDo(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     /* Emit an annotated nop so we know to decompile a 'do' keyword. */
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
         return false;
 
     ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex2 < 0)
@@ -4668,17 +4678,17 @@ EmitDo(JSContext *cx, BytecodeEmitter *b
         return false;
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, 1 + (off - top)))
         return false;
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitWhile(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     /*
      * Minimize bytecodes issued for one or more iterations by jumping to
      * the condition below the body and closing the loop if the condition
      * is true with a backward branch. For iteration count i:
      *
      *  i    test at the top                 test at the bottom
      *  =    ===============                 ==================
@@ -4721,17 +4731,17 @@ EmitWhile(JSContext *cx, BytecodeEmitter
 
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
         return false;
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitBreak(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
+EmitBreak(ExclusiveContext *cx, BytecodeEmitter *bce, PropertyName *label)
 {
     StmtInfoBCE *stmt = bce->topStmt;
     SrcNoteType noteType;
     if (label) {
         while (stmt->type != STMT_LABEL || stmt->label != label)
             stmt = stmt->down;
         noteType = SRC_BREAK2LABEL;
     } else {
@@ -4739,17 +4749,17 @@ EmitBreak(JSContext *cx, BytecodeEmitter
             stmt = stmt->down;
         noteType = (stmt->type == STMT_SWITCH) ? SRC_SWITCHBREAK : SRC_BREAK;
     }
 
     return EmitGoto(cx, bce, stmt, &stmt->breaks, noteType) >= 0;
 }
 
 static bool
-EmitContinue(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
+EmitContinue(ExclusiveContext *cx, BytecodeEmitter *bce, PropertyName *label)
 {
     StmtInfoBCE *stmt = bce->topStmt;
     if (label) {
         /* Find the loop statement enclosed by the matching label. */
         StmtInfoBCE *loop = NULL;
         while (stmt->type != STMT_LABEL || stmt->label != label) {
             if (stmt->isLoop())
                 loop = stmt;
@@ -4760,17 +4770,17 @@ EmitContinue(JSContext *cx, BytecodeEmit
         while (!stmt->isLoop())
             stmt = stmt->down;
     }
 
     return EmitGoto(cx, bce, stmt, &stmt->continues, SRC_CONTINUE) >= 0;
 }
 
 static bool
-EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitReturn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
         return false;
 
     /* Push a return value */
     if (ParseNode *pn2 = pn->pn_kid) {
         if (!EmitTree(cx, bce, pn2))
             return false;
@@ -4802,17 +4812,17 @@ EmitReturn(JSContext *cx, BytecodeEmitte
         if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitStatementList(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitStatementList(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     JS_ASSERT(pn->isArity(PN_LIST));
 
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_BLOCK, top);
 
     ParseNode *pnchild = pn->pn_head;
 
@@ -4823,17 +4833,17 @@ EmitStatementList(JSContext *cx, Bytecod
         if (!EmitTree(cx, bce, pn2))
             return false;
     }
 
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitStatement(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitStatement(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_SEMI));
 
     ParseNode *pn2 = pn->pn_kid;
     if (!pn2)
         return true;
 
     if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
@@ -4899,17 +4909,17 @@ EmitStatement(JSContext *cx, BytecodeEmi
         if (!bce->reportStrictWarning(pn2, JSMSG_USELESS_EXPR))
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitDelete(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     /*
      * Under ECMA 3, deleting a non-reference returns true -- but alas we
      * must evaluate the operand if it appears it might have side effects.
      */
     ParseNode *pn2 = pn->pn_kid;
     switch (pn2->getKind()) {
       case PNK_NAME:
@@ -4956,17 +4966,17 @@ EmitDelete(JSContext *cx, BytecodeEmitte
             return false;
       }
     }
 
     return true;
 }
 
 static bool
-EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     bool callop = pn->isKind(PNK_CALL);
 
     /*
      * Emit callable invocation or operator new (constructor call) code.
      * First, emit code for the left operand to evaluate the callable or
      * constructable object expression.
      *
@@ -4978,18 +4988,19 @@ EmitCallOrNew(JSContext *cx, BytecodeEmi
      * Then (or in a call case that has no explicit reference-base
      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
      * value required for calls (which non-strict mode functions
      * will box into the global object).
      */
     uint32_t argc = pn->pn_count - 1;
 
     if (argc >= ARGC_LIMIT) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
+        bce->parser->tokenStream.reportError(callop
+                                             ? JSMSG_TOO_MANY_FUN_ARGS
+                                             : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
     bool emitArgs = true;
     ParseNode *pn2 = pn->pn_head;
     switch (pn2->getKind()) {
       case PNK_NAME:
         if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
@@ -5106,17 +5117,17 @@ EmitCallOrNew(JSContext *cx, BytecodeEmi
     if (pn->pn_xflags & PNX_SETCALL) {
         if (Emit1(cx, bce, JSOP_SETCALL) < 0)
             return false;
     }
     return true;
 }
 
 static bool
-EmitLogical(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     /*
      * JSOP_OR converts the operand on the stack to boolean, leaves the original
      * value on the stack and jumps if true; otherwise it falls into the next
      * bytecode, which pops the left operand and then evaluates the right operand.
      * The jump goes around the right operand evaluation.
      *
      * JSOP_AND converts the operand on the stack to boolean and jumps if false;
@@ -5182,17 +5193,17 @@ EmitLogical(JSContext *cx, BytecodeEmitt
     return true;
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitIncOrDec(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     /* Emit lvalue-specialized code for ++/-- operators. */
     ParseNode *pn2 = pn->pn_kid;
     switch (pn2->getKind()) {
       case PNK_DOT:
         if (!EmitPropIncDec(cx, pn, bce))
             return false;
         break;
@@ -5261,17 +5272,17 @@ EmitIncOrDec(JSContext *cx, BytecodeEmit
     return true;
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitLabeledStatement(JSContext *cx, BytecodeEmitter *bce, const LabeledStatement *pn)
+EmitLabeledStatement(ExclusiveContext *cx, BytecodeEmitter *bce, const LabeledStatement *pn)
 {
     /*
      * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
      * following the labeled statement.
      */
     jsatomid index;
     if (!bce->makeAtomIndex(pn->label(), &index))
         return false;
@@ -5290,33 +5301,33 @@ EmitLabeledStatement(JSContext *cx, Byte
         return false;
 
     /* Patch the JSOP_LABEL offset. */
     SetJumpOffsetAt(bce, top);
     return true;
 }
 
 static bool
-EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+EmitSyntheticStatements(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     JS_ASSERT(pn->isArity(PN_LIST));
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_SEQ, top);
     ParseNode *pn2 = pn->pn_head;
     if (pn->pn_xflags & PNX_DESTRUCT)
         pn2 = pn2->pn_next;
     for (; pn2; pn2 = pn2->pn_next) {
         if (!EmitTree(cx, bce, pn2))
             return false;
     }
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
+EmitConditionalExpression(ExclusiveContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
 {
     /* Emit the condition, then branch if false to the else part. */
     if (!EmitTree(cx, bce, &conditional.condition()))
         return false;
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
     if (noteIndex < 0)
         return false;
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
@@ -5347,17 +5358,17 @@ EmitConditionalExpression(JSContext *cx,
     return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
 }
 
 /*
  * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
  * the comment on EmitSwitch.
  */
 MOZ_NEVER_INLINE static bool
-EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
 #if JS_HAS_DESTRUCTURING_SHORTHAND
     if (pn->pn_xflags & PNX_DESTRUCT) {
         bce->reportError(pn, JSMSG_BAD_OBJECT_INIT);
         return false;
     }
 #endif
 
@@ -5465,17 +5476,17 @@ EmitObject(JSContext *cx, BytecodeEmitte
                           "newinit and newobject must have equal length to edit in-place");
         EMIT_UINT32_IN_PLACE(offset, JSOP_NEWOBJECT, uint32_t(index));
     }
 
     return true;
 }
 
 static bool
-EmitArray(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     /*
      * Emit code for [a, b, c] that is equivalent to constructing a new
      * array and in source order evaluating each element value and adding
      * it to the array, without invoking latent setters.  We use the
      * JSOP_NEWINIT and JSOP_INITELEM_ARRAY bytecodes to ignore setters and
      * to avoid dup'ing and popping the array as each element is added, as
      * JSOP_SETELEM/JSOP_SETPROP would do.
@@ -5552,17 +5563,17 @@ EmitArray(JSContext *cx, BytecodeEmitter
             return false;
     }
 
     /* Emit an op to finish the array and aid in decompilation. */
     return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
 }
 
 static bool
-EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitUnary(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
         return false;
     /* Unary op, including unary +/-. */
     JSOp op = pn->getOp();
     ParseNode *pn2 = pn->pn_kid;
 
     if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
@@ -5573,17 +5584,17 @@ EmitUnary(JSContext *cx, BytecodeEmitter
     if (!EmitTree(cx, bce, pn2))
         return false;
 
     bce->emittingForInit = oldEmittingForInit;
     return Emit1(cx, bce, op) >= 0;
 }
 
 static bool
-EmitDefaults(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitDefaults(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_ARGSBODY));
 
     ParseNode *arg, *pnlast = pn->last();
     for (arg = pn->pn_head; arg != pnlast; arg = arg->pn_next) {
         if (!(arg->pn_dflags & PND_DEFAULT) || !arg->isKind(PNK_NAME))
             continue;
         if (!BindNameToSlot(cx, bce, arg))
@@ -5608,17 +5619,17 @@ EmitDefaults(JSContext *cx, BytecodeEmit
             return false;
         SET_JUMP_OFFSET(bce->code(jump), bce->offset() - jump);
     }
 
     return true;
 }
 
 bool
-frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     EmitLevelManager elm(bce);
 
     bool ok = true;
     ptrdiff_t top = bce->offset();
     pn->pn_offset = top;
@@ -6022,32 +6033,32 @@ frontend::EmitTree(JSContext *cx, Byteco
         if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.end))
             return false;
     }
 
     return ok;
 }
 
 static int
-AllocSrcNote(JSContext *cx, SrcNotesVector &notes)
+AllocSrcNote(ExclusiveContext *cx, SrcNotesVector &notes)
 {
     // Start it off moderately large to avoid repeated resizings early on.
     if (notes.capacity() == 0 && !notes.reserve(1024))
         return -1;
 
     jssrcnote dummy = 0;
     if (!notes.append(dummy)) {
         js_ReportOutOfMemory(cx);
         return -1;
     }
     return notes.length() - 1;
 }
 
 int
-frontend::NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type)
+frontend::NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type)
 {
     SrcNotesVector &notes = bce->notes();
     int index;
 
     index = AllocSrcNote(cx, notes);
     if (index < 0)
         return -1;
 
@@ -6078,46 +6089,46 @@ frontend::NewSrcNote(JSContext *cx, Byte
     for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
         if (NewSrcNote(cx, bce, SRC_NULL) < 0)
             return -1;
     }
     return index;
 }
 
 int
-frontend::NewSrcNote2(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
+frontend::NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
 {
     int index;
 
     index = NewSrcNote(cx, bce, type);
     if (index >= 0) {
         if (!SetSrcNoteOffset(cx, bce, index, 0, offset))
             return -1;
     }
     return index;
 }
 
 int
-frontend::NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
+frontend::NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
             ptrdiff_t offset2)
 {
     int index;
 
     index = NewSrcNote(cx, bce, type);
     if (index >= 0) {
         if (!SetSrcNoteOffset(cx, bce, index, 0, offset1))
             return -1;
         if (!SetSrcNoteOffset(cx, bce, index, 1, offset2))
             return -1;
     }
     return index;
 }
 
 bool
-frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
+frontend::AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
 {
     /*
      * Called only from FinishTakingSrcNotes to add to main script note
      * deltas, and only by a small positive amount.
      */
     JS_ASSERT(bce->current == &bce->main);
     JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
 
@@ -6131,21 +6142,21 @@ frontend::AddToSrcNoteDelta(JSContext *c
         SN_MAKE_XDELTA(&xdelta, delta);
         if (!(sn = bce->main.notes.insert(sn, xdelta)))
             return false;
     }
     return true;
 }
 
 static bool
-SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which,
+SetSrcNoteOffset(ExclusiveContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which,
                  ptrdiff_t offset)
 {
     if (size_t(offset) > SN_MAX_OFFSET) {
-        ReportStatementTooLarge(cx, bce->topStmt);
+        ReportStatementTooLarge(bce->parser->tokenStream, bce->topStmt);
         return false;
     }
 
     SrcNotesVector &notes = bce->notes();
 
     /* Find the offset numbered which (i.e., skip exactly which offsets). */
     jssrcnote *sn = notes.begin() + index;
     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
@@ -6213,17 +6224,17 @@ DumpSrcNoteSizeHist()
 /*
  * Fill in the storage at notes with prolog and main srcnotes; the space at
  * notes was allocated using the BytecodeEmitter::countFinalSourceNotes()
  * method from BytecodeEmitter.h. SO DON'T CHANGE THIS FUNCTION WITHOUT AT
  * LEAST CHECKING WHETHER BytecodeEmitter::countFinalSourceNotes() NEEDS
  * CORRESPONDING CHANGES!
  */
 bool
-frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes)
+frontend::FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *notes)
 {
     JS_ASSERT(bce->current == &bce->main);
 
     unsigned prologCount = bce->prolog.notes.length();
     if (prologCount && bce->prolog.currentLine != bce->firstLine) {
         bce->switchToProlog();
         if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
             return false;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -21,17 +21,17 @@
 
 #include "vm/ScopeObject.h"
 
 namespace js {
 namespace frontend {
 
 struct CGTryNoteList {
     Vector<JSTryNote> list;
-    CGTryNoteList(JSContext *cx) : list(cx) {}
+    CGTryNoteList(ExclusiveContext *cx) : list(cx) {}
 
     bool append(JSTryNoteKind kind, unsigned stackDepth, size_t start, size_t end);
     size_t length() const { return list.length(); }
     void finish(TryNoteArray *array);
 };
 
 struct CGObjectList {
     uint32_t            length;     /* number of emitted so far objects */
@@ -42,17 +42,17 @@ struct CGObjectList {
     unsigned add(ObjectBox *objbox);
     unsigned indexOf(JSObject *obj);
     void finish(ObjectArray *array);
 };
 
 class CGConstList {
     Vector<Value> list;
   public:
-    CGConstList(JSContext *cx) : list(cx) {}
+    CGConstList(ExclusiveContext *cx) : list(cx) {}
     bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); }
     size_t length() const { return list.length(); }
     void finish(ConstArray *array);
 };
 
 struct StmtInfoBCE;
 
 // Use zero inline elements because these go on the stack and affect how many
@@ -73,17 +73,17 @@ struct BytecodeEmitter
     struct EmitSection {
         BytecodeVector code;        /* bytecode */
         SrcNotesVector notes;       /* source notes, see below */
         ptrdiff_t   lastNoteOffset; /* code offset for last source note */
         uint32_t    currentLine;    /* line number for tree-based srcnote gen */
         uint32_t    lastColumn;     /* zero-based column index on currentLine of
                                        last SRC_COLSPAN-annotated opcode */
 
-        EmitSection(JSContext *cx, uint32_t lineNum)
+        EmitSection(ExclusiveContext *cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0)
         {}
     };
     EmitSection prolog, main, *current;
 
     /* the parser */
     Parser<FullParseHandler> *const parser;
 
@@ -175,17 +175,17 @@ struct BytecodeEmitter
         return true;
     }
 
     bool isInLoop();
     bool checkSingletonContext();
 
     bool needsImplicitThis();
 
-    void tellDebuggerAboutCompiledScript(JSContext *cx);
+    void tellDebuggerAboutCompiledScript(ExclusiveContext *cx);
 
     inline TokenStream *tokenStream();
 
     BytecodeVector &code() const { return current->code; }
     jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; }
     ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
     ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); }
     void switchToMain() { current = &main; }
@@ -202,70 +202,70 @@ struct BytecodeEmitter
     bool reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...);
     bool reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...);
 };
 
 /*
  * Emit one bytecode.
  */
 ptrdiff_t
-Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op);
+Emit1(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op);
 
 /*
  * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1).
  */
 ptrdiff_t
-Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1);
+Emit2(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1);
 
 /*
  * Emit three bytecodes, an opcode with two bytes of immediate operands.
  */
 ptrdiff_t
-Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2);
+Emit3(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2);
 
 /*
  * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
  */
 ptrdiff_t
-EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra);
+EmitN(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra);
 
 /*
  * Emit code into bce for the tree rooted at pn.
  */
 bool
-EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn);
+EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn);
 
 /*
  * Emit function code using bce for the tree rooted at body.
  */
 bool
-EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body);
+EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body);
 
 /*
  * Append a new source note of the given type (and therefore size) to bce's
  * notes dynamic array, updating bce->noteCount. Return the new note's index
  * within the array pointed at by bce->current->notes. Return -1 if out of
  * memory.
  */
 int
-NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type);
+NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type);
 
 int
-NewSrcNote2(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset);
+NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset);
 
 int
-NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
+NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
                ptrdiff_t offset2);
 
 /* NB: this function can add at most one extra extended delta note. */
 bool
-AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta);
+AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta);
 
 bool
-FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes);
+FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *notes);
 
 /*
  * Finish taking source notes in cx's notePool, copying final notes to the new
  * stable store allocated by the caller and passed in via notes. Return false
  * on malloc failure, which means this function reported an error.
  *
  * Use this to compute the number of jssrcnotes to allocate and pass in via
  * notes. This method knows a lot about details of FinishTakingSrcNotes, so
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/TypedEnum.h"
 
 #include "jslibmath.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "vm/NumericConversions.h"
 
+#include "jscntxtinlines.h"
+
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 
 static ParseNode *
 ContainsVarOrConst(ParseNode *pn)
@@ -62,17 +64,17 @@ ContainsVarOrConst(ParseNode *pn)
     return NULL;
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
 static bool
-FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
+FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind)
 {
     if (!pn->isKind(kind)) {
         switch (kind) {
           case PNK_NUMBER:
             if (pn->isKind(PNK_STRING)) {
                 double d;
                 if (!StringToNumber(cx, pn->pn_atom, &d))
                     return false;
@@ -102,17 +104,17 @@ FoldType(JSContext *cx, ParseNode *pn, P
 }
 
 /*
  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
  * a successful call to this function.
  */
 static bool
-FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
+FoldBinaryNumeric(ExclusiveContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
                   ParseNode *pn)
 {
     double d, d2;
     int32_t i, j;
 
     JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
     d = pn1->pn_dval;
     d2 = pn2->pn_dval;
@@ -239,40 +241,41 @@ MOZ_END_ENUM_CLASS(SyntacticContext)
 
 static SyntacticContext
 condIf(const ParseNode *pn, ParseNodeKind kind)
 {
     return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other;
 }
 
 static bool
-Fold(JSContext *cx, ParseNode **pnp, FullParseHandler &handler, bool inGenexpLambda,
-     SyntacticContext sc)
+Fold(ExclusiveContext *cx, ParseNode **pnp,
+     FullParseHandler &handler, const CompileOptions &options,
+     bool inGenexpLambda, SyntacticContext sc)
 {
     ParseNode *pn = *pnp;
     ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
 
     JS_CHECK_RECURSION(cx, return false);
 
     // First, recursively fold constants on the children of this node.
     switch (pn->getArity()) {
       case PN_CODE:
         if (pn->isKind(PNK_FUNCTION) &&
-            pn->pn_funbox->useAsmOrInsideUseAsm() && cx->hasOption(JSOPTION_ASMJS))
+            pn->pn_funbox->useAsmOrInsideUseAsm() && options.asmJSOption)
         {
             return true;
         }
         if (pn->getKind() == PNK_MODULE) {
-            if (!Fold(cx, &pn->pn_body, handler, false, SyntacticContext::Other))
+            if (!Fold(cx, &pn->pn_body, handler, options, false, SyntacticContext::Other))
                 return false;
         } else {
             // Note: pn_body is NULL for functions which are being lazily parsed.
             JS_ASSERT(pn->getKind() == PNK_FUNCTION);
             if (pn->pn_body) {
-                if (!Fold(cx, &pn->pn_body, handler, pn->pn_funbox->inGenexpLambda,
+                if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda,
                           SyntacticContext::Other))
                     return false;
             }
         }
         break;
 
       case PN_LIST:
       {
@@ -282,71 +285,71 @@ Fold(JSContext *cx, ParseNode **pnp, Ful
             kidsc = sc;
 
         // Don't fold a parenthesized call expression. See bug 537673.
         ParseNode **listp = &pn->pn_head;
         if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_NEW)) && (*listp)->isInParens())
             listp = &(*listp)->pn_next;
 
         for (; *listp; listp = &(*listp)->pn_next) {
-            if (!Fold(cx, listp, handler, inGenexpLambda, kidsc))
+            if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc))
                 return false;
         }
 
         // If the last node in the list was replaced, pn_tail points into the wrong node.
         pn->pn_tail = listp;
 
         // Save the list head in pn1 for later use.
         pn1 = pn->pn_head;
         pn2 = NULL;
         break;
       }
 
       case PN_TERNARY:
         /* Any kid may be null (e.g. for (;;)). */
         if (pn->pn_kid1) {
-            if (!Fold(cx, &pn->pn_kid1, handler, inGenexpLambda, condIf(pn, PNK_IF)))
+            if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF)))
                 return false;
         }
         pn1 = pn->pn_kid1;
 
         if (pn->pn_kid2) {
-            if (!Fold(cx, &pn->pn_kid2, handler, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
+            if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
                 return false;
             if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) {
                 handler.freeTree(pn->pn_kid2);
                 pn->pn_kid2 = NULL;
             }
         }
         pn2 = pn->pn_kid2;
 
         if (pn->pn_kid3) {
-            if (!Fold(cx, &pn->pn_kid3, handler, inGenexpLambda, SyntacticContext::Other))
+            if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
                 return false;
         }
         pn3 = pn->pn_kid3;
         break;
 
       case PN_BINARY:
         if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
             // Propagate Condition context through logical connectives.
             SyntacticContext kidsc = SyntacticContext::Other;
             if (sc == SyntacticContext::Condition)
                 kidsc = sc;
-            if (!Fold(cx, &pn->pn_left, handler, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc))
                 return false;
-            if (!Fold(cx, &pn->pn_right, handler, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc))
                 return false;
         } else {
             /* First kid may be null (for default case in switch). */
             if (pn->pn_left) {
-                if (!Fold(cx, &pn->pn_left, handler, inGenexpLambda, condIf(pn, PNK_WHILE)))
+                if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE)))
                     return false;
             }
-            if (!Fold(cx, &pn->pn_right, handler, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
+            if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
                 return false;
         }
         pn1 = pn->pn_left;
         pn2 = pn->pn_right;
         break;
 
       case PN_UNARY:
         /*
@@ -363,34 +366,34 @@ Fold(JSContext *cx, ParseNode **pnp, Ful
 
         if (pn->pn_kid) {
             SyntacticContext kidsc =
                 pn->isKind(PNK_NOT)
                 ? SyntacticContext::Condition
                 : pn->isKind(PNK_DELETE)
                 ? SyntacticContext::Delete
                 : SyntacticContext::Other;
-            if (!Fold(cx, &pn->pn_kid, handler, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
                 return false;
         }
         pn1 = pn->pn_kid;
         break;
 
       case PN_NAME:
         /*
          * Skip pn1 down along a chain of dotted member expressions to avoid
          * excessive recursion.  Our only goal here is to fold constants (if
          * any) in the primary expression operand to the left of the first
          * dot in the chain.
          */
         if (!pn->isUsed()) {
             ParseNode **lhsp = &pn->pn_expr;
             while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed())
                 lhsp = &(*lhsp)->pn_expr;
-            if (*lhsp && !Fold(cx, lhsp, handler, inGenexpLambda, SyntacticContext::Other))
+            if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other))
                 return false;
             pn1 = *lhsp;
         }
         break;
 
       case PN_NULLARY:
         break;
     }
@@ -799,20 +802,19 @@ Fold(JSContext *cx, ParseNode **pnp, Ful
             pn->setArity(PN_NULLARY);
         }
     }
 
     return true;
 }
 
 bool
-frontend::FoldConstants(JSContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser)
+frontend::FoldConstants(ExclusiveContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser)
 {
-
     // Don't fold constants if the code has requested "use asm" as
     // constant-folding will misrepresent the source text for the purpose
     // of type checking. (Also guard against entering a function containing
     // "use asm", see PN_FUNC case below.)
-    if (parser->pc->useAsmOrInsideUseAsm() && cx->hasOption(JSOPTION_ASMJS))
+    if (parser->pc->useAsmOrInsideUseAsm() && parser->options().asmJSOption)
         return true;
 
-    return Fold(cx, pnp, parser->handler, false, SyntacticContext::Other);
+    return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other);
 }
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -23,20 +23,21 @@ namespace frontend {
 //
 // Usage:
 //    pn = parser->statement();
 //    if (!pn)
 //        return false;
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
 bool
-FoldConstants(JSContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser);
+FoldConstants(ExclusiveContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser);
 
 inline bool
-FoldConstants(JSContext *cx, SyntaxParseHandler::Node *pnp, Parser<SyntaxParseHandler> *parser)
+FoldConstants(ExclusiveContext *cx, SyntaxParseHandler::Node *pnp,
+              Parser<SyntaxParseHandler> *parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -64,19 +64,20 @@ class FullParseHandler
     Parser<SyntaxParseHandler> *syntaxParser;
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode *Node;
     typedef Definition *DefinitionNode;
 
-    FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
+    FullParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
+                     TokenStream &tokenStream, bool foldConstants,
                      Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
-      : allocator(cx),
+      : allocator(cx, alloc),
         tokenStream(tokenStream),
         foldConstants(foldConstants),
         lazyOuterFunction_(lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
 
     static ParseNode *null() { return NULL; }
--- a/js/src/frontend/ParseMaps-inl.h
+++ b/js/src/frontend/ParseMaps-inl.h
@@ -11,46 +11,46 @@
 
 #include "frontend/ParseMaps.h"
 
 namespace js {
 namespace frontend {
 
 template <class Map>
 inline bool
-AtomThingMapPtr<Map>::ensureMap(JSContext *cx)
+AtomThingMapPtr<Map>::ensureMap(ExclusiveContext *cx)
 {
     if (map_)
         return true;
-    map_ = cx->runtime()->parseMapPool.acquire<Map>();
+    map_ = cx->parseMapPool().acquire<Map>();
     return !!map_;
 }
 
 template <class Map>
 inline void
-AtomThingMapPtr<Map>::releaseMap(JSContext *cx)
+AtomThingMapPtr<Map>::releaseMap(ExclusiveContext *cx)
 {
     if (!map_)
         return;
-    cx->runtime()->parseMapPool.release(map_);
+    cx->parseMapPool().release(map_);
     map_ = NULL;
 }
 
 template <typename ParseHandler>
 inline bool
 AtomDecls<ParseHandler>::init()
 {
-    map = cx->runtime()->parseMapPool.acquire<AtomDefnListMap>();
+    map = cx->parseMapPool().acquire<AtomDefnListMap>();
     return map;
 }
 
 template <typename ParseHandler>
 inline
 AtomDecls<ParseHandler>::~AtomDecls()
 {
     if (map)
-        cx->runtime()->parseMapPool.release(map);
+        cx->parseMapPool().release(map);
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_ParseMaps_inl_h */
--- a/js/src/frontend/ParseMaps.cpp
+++ b/js/src/frontend/ParseMaps.cpp
@@ -53,19 +53,19 @@ ParseMapPool::allocateFresh()
     if (!map)
         return NULL;
 
     all.infallibleAppend(map);
     return (void *) map;
 }
 
 DefinitionList::Node *
-DefinitionList::allocNode(JSContext *cx, uintptr_t head, Node *tail)
+DefinitionList::allocNode(ExclusiveContext *cx, LifoAlloc &alloc, uintptr_t head, Node *tail)
 {
-    Node *result = cx->tempLifoAlloc().new_<Node>(head, tail);
+    Node *result = alloc.new_<Node>(head, tail);
     if (!result)
         js_ReportOutOfMemory(cx);
     return result;
 }
 
 #ifdef DEBUG
 template <typename ParseHandler>
 void
@@ -100,21 +100,21 @@ DumpAtomDefnMap(const AtomDefnMapPtr &ma
 template <typename ParseHandler>
 bool
 AtomDecls<ParseHandler>::addShadow(JSAtom *atom, typename ParseHandler::DefinitionNode defn)
 {
     AtomDefnListAddPtr p = map->lookupForAdd(atom);
     if (!p)
         return map->add(p, atom, DefinitionList(ParseHandler::definitionToBits(defn)));
 
-    return p.value().pushFront<ParseHandler>(cx, defn);
+    return p.value().pushFront<ParseHandler>(cx, alloc, defn);
 }
 
 void
-frontend::InitAtomMap(JSContext *cx, frontend::AtomIndexMap *indices, HeapPtrAtom *atoms)
+frontend::InitAtomMap(frontend::AtomIndexMap *indices, HeapPtrAtom *atoms)
 {
     if (indices->isMap()) {
         typedef AtomIndexMap::WordMap WordMap;
         const WordMap &wm = indices->asMap();
         for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key;
             jsatomid index = r.front().value;
             JS_ASSERT(index < indices->count());
--- a/js/src/frontend/ParseMaps.h
+++ b/js/src/frontend/ParseMaps.h
@@ -25,17 +25,17 @@ typedef InlineMap<JSAtom *, DefinitionSi
 typedef InlineMap<JSAtom *, DefinitionList, 24> AtomDefnListMap;
 
 /*
  * For all unmapped atoms recorded in al, add a mapping from the atom's index
  * to its address. map->length must already be set to the number of atoms in
  * the list and map->vector must point to pre-allocated memory.
  */
 void
-InitAtomMap(JSContext *cx, AtomIndexMap *indices, HeapPtr<JSAtom> *atoms);
+InitAtomMap(AtomIndexMap *indices, HeapPtr<JSAtom> *atoms);
 
 /*
  * A pool that permits the reuse of the backing storage for the defn, index, or
  * defn-or-header (multi) maps.
  *
  * The pool owns all the maps that are given out, and is responsible for
  * relinquishing all resources when |purgeAll| is triggered.
  */
@@ -122,18 +122,18 @@ class ParseMapPool
  */
 template <class Map>
 struct AtomThingMapPtr
 {
     Map *map_;
 
     void init() { clearMap(); }
 
-    bool ensureMap(JSContext *cx);
-    void releaseMap(JSContext *cx);
+    bool ensureMap(ExclusiveContext *cx);
+    void releaseMap(ExclusiveContext *cx);
 
     bool hasMap() const { return map_; }
     Map *getMap() { return map_; }
     void setMap(Map *newMap) { JS_ASSERT(!map_); map_ = newMap; }
     void clearMap() { map_ = NULL; }
 
     Map *operator->() { return map_; }
     const Map *operator->() const { return map_; }
@@ -144,20 +144,20 @@ typedef AtomThingMapPtr<AtomIndexMap> At
 
 /*
  * Wrapper around an AtomThingMapPtr (or its derivatives) that automatically
  * releases a map on destruction, if one has been acquired.
  */
 template <typename AtomThingMapPtrT>
 class OwnedAtomThingMapPtr : public AtomThingMapPtrT
 {
-    JSContext *cx;
+    ExclusiveContext *cx;
 
   public:
-    explicit OwnedAtomThingMapPtr(JSContext *cx) : cx(cx) {
+    explicit OwnedAtomThingMapPtr(ExclusiveContext *cx) : cx(cx) {
         AtomThingMapPtrT::init();
     }
 
     ~OwnedAtomThingMapPtr() {
         AtomThingMapPtrT::releaseMap(cx);
     }
 };
 
@@ -239,18 +239,18 @@ class DefinitionList
     } u;
 
     Node *firstNode() const {
         JS_ASSERT(isMultiple());
         return (Node *) (u.bits & ~0x1);
     }
 
     static Node *
-    allocNode(JSContext *cx, uintptr_t bits, Node *tail);
-            
+    allocNode(ExclusiveContext *cx, LifoAlloc &alloc, uintptr_t bits, Node *tail);
+
   public:
     class Range
     {
         friend class DefinitionList;
 
         Node *node;
         uintptr_t bits;
 
@@ -331,27 +331,28 @@ class DefinitionList
     }
 
     /*
      * Add a definition to the front of this list.
      *
      * Return true on success. On OOM, report on cx and return false.
      */
     template <typename ParseHandler>
-    bool pushFront(JSContext *cx, typename ParseHandler::DefinitionNode defn) {
+    bool pushFront(ExclusiveContext *cx, LifoAlloc &alloc,
+                   typename ParseHandler::DefinitionNode defn) {
         Node *tail;
         if (isMultiple()) {
             tail = firstNode();
         } else {
-            tail = allocNode(cx, u.bits, NULL);
+            tail = allocNode(cx, alloc, u.bits, NULL);
             if (!tail)
                 return false;
         }
 
-        Node *node = allocNode(cx, ParseHandler::definitionToBits(defn), tail);
+        Node *node = allocNode(cx, alloc, ParseHandler::definitionToBits(defn), tail);
         if (!node)
             return false;
         *this = DefinitionList(node);
         return true;
     }
 
     /* Overwrite the first Definition in the list. */
     template <typename ParseHandler>
@@ -399,24 +400,25 @@ typedef AtomDefnListMap::Range  AtomDefn
 template <typename ParseHandler>
 class AtomDecls
 {
     typedef typename ParseHandler::DefinitionNode DefinitionNode;
 
     /* AtomDeclsIter needs to get at the DefnListMap directly. */
     friend class AtomDeclsIter;
 
-    JSContext   *cx;
+    ExclusiveContext *cx;
+    LifoAlloc &alloc;
     AtomDefnListMap  *map;
 
     AtomDecls(const AtomDecls &other) MOZ_DELETE;
     void operator=(const AtomDecls &other) MOZ_DELETE;
 
   public:
-    explicit AtomDecls(JSContext *cx) : cx(cx), map(NULL) {}
+    explicit AtomDecls(ExclusiveContext *cx, LifoAlloc &alloc) : cx(cx), alloc(alloc), map(NULL) {}
 
     ~AtomDecls();
 
     bool init();
 
     void clear() {
         map->clear();
     }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -3,16 +3,18 @@
  * 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/. */
 
 #include "builtin/Module.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 
+#include "jscntxtinlines.h"
+
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsFinite;
 
 /*
  * Asserts to verify assumptions behind pn_ macros.
  */
@@ -235,17 +237,17 @@ ParseNodeAllocator::freeTree(ParseNode *
 void *
 ParseNodeAllocator::allocNode()
 {
     if (ParseNode *pn = freelist) {
         freelist = pn->pn_next;
         return pn;
     }
 
-    void *p = cx->tempLifoAlloc().alloc(sizeof (ParseNode));
+    void *p = alloc.alloc(sizeof (ParseNode));
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 /* used only by static create methods of subclasses */
 
 ParseNode *
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -46,25 +46,23 @@ class UpvarCookie
 
     static bool isLevelReserved(uint16_t level) { return level == FREE_LEVEL; }
 
     bool isFree() const { return level_ == FREE_LEVEL; }
     uint16_t level() const { JS_ASSERT(!isFree()); return level_; }
     uint16_t slot()  const { JS_ASSERT(!isFree()); return slot_; }
 
     // This fails and issues an error message if newLevel is too large.
-    bool set(JSContext *cx, unsigned newLevel, uint16_t newSlot) {
+    bool set(TokenStream &ts, unsigned newLevel, uint16_t newSlot) {
         // 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 (newLevel >= FREE_LEVEL) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_DEEP, js_function_str);
-            return false;
-        }
+        if (newLevel >= FREE_LEVEL)
+            return ts.reportError(JSMSG_TOO_DEEP);
         level_ = newLevel;
         slot_ = newSlot;
         return true;
     }
 
     void makeFree() {
         level_ = FREE_LEVEL;
         slot_ = 0;      // value doesn't matter, won't be used
@@ -798,17 +796,17 @@ class ParseNode
     }
 
     void checkListConsistency()
 #ifndef DEBUG
     {}
 #endif
     ;
 
-    bool getConstantValue(JSContext *cx, bool strictChecks, MutableHandleValue vp);
+    bool getConstantValue(ExclusiveContext *cx, bool strictChecks, MutableHandleValue vp);
     inline bool isConstant();
 
     template <class NodeType>
     inline bool is() const {
         return NodeType::test(*this);
     }
 
     /* Casting operations. */
@@ -1352,25 +1350,28 @@ struct Definition : public ParseNode
             return LET;
         return VAR;
     }
 };
 
 class ParseNodeAllocator
 {
   public:
-    explicit ParseNodeAllocator(JSContext *cx) : cx(cx), freelist(NULL) {}
+    explicit ParseNodeAllocator(ExclusiveContext *cx, LifoAlloc &alloc)
+      : cx(cx), alloc(alloc), freelist(NULL)
+    {}
 
     void *allocNode();
     void freeNode(ParseNode *pn);
     ParseNode *freeTree(ParseNode *pn);
     void prepareNodeForMutation(ParseNode *pn);
 
   private:
-    JSContext *cx;
+    ExclusiveContext *cx;
+    LifoAlloc &alloc;
     ParseNode *freelist;
 };
 
 inline bool
 ParseNode::test(unsigned flag) const
 {
     JS_ASSERT(pn_defn || pn_arity == PN_CODE || pn_arity == PN_NAME);
 #ifdef DEBUG
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -72,17 +72,20 @@ typedef MutableHandle<PropertyName*> Mut
     JS_END_MACRO
 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
 
 template <typename ParseHandler>
 bool
 GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid)
 {
     if (pc->blockidGen == JS_BIT(20)) {
-        JS_ReportErrorNumber(pc->sc->context, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
+        if (!pc->sc->context->isJSContext())
+            return false;
+        JS_ReportErrorNumber(pc->sc->context->asJSContext(),
+                             js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
         return false;
     }
     JS_ASSERT(pc->blockidGen < JS_BIT(20));
     blockid = pc->blockidGen++;
     return true;
 }
 
 template bool
@@ -97,18 +100,18 @@ PushStatementPC(ParseContext<ParseHandle
 {
     stmt->blockid = pc->blockid();
     PushStatement(pc, stmt, type);
 }
 
 // See comment on member function declaration.
 template <>
 bool
-ParseContext<FullParseHandler>::define(JSContext *cx, HandlePropertyName name,
-                                       ParseNode *pn, Definition::Kind kind)
+ParseContext<FullParseHandler>::define(TokenStream &ts,
+                                       PropertyName *name, ParseNode *pn, Definition::Kind kind)
 {
     JS_ASSERT(!pn->isUsed());
     JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
 
     Definition *prevDef = NULL;
     if (kind == Definition::LET)
         prevDef = decls_.lookupFirst(name);
     else
@@ -149,32 +152,32 @@ ParseContext<FullParseHandler>::define(J
         pn->pn_dflags |= PND_CONST;
 
     Definition *dn = (Definition *)pn;
     switch (kind) {
       case Definition::ARG:
         JS_ASSERT(sc->isFunctionBox());
         dn->setOp(JSOP_GETARG);
         dn->pn_dflags |= PND_BOUND;
-        if (!dn->pn_cookie.set(cx, staticLevel, args_.length()))
+        if (!dn->pn_cookie.set(ts, staticLevel, args_.length()))
             return false;
         if (!args_.append(dn))
             return false;
-        if (name == cx->names().empty)
+        if (name == ts.names().empty)
             break;
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
       case Definition::CONST:
       case Definition::VAR:
         if (sc->isFunctionBox()) {
             dn->setOp(JSOP_GETLOCAL);
             dn->pn_dflags |= PND_BOUND;
-            if (!dn->pn_cookie.set(cx, staticLevel, vars_.length()))
+            if (!dn->pn_cookie.set(ts, staticLevel, vars_.length()))
                 return false;
             if (!vars_.append(dn))
                 return false;
         }
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
@@ -190,17 +193,17 @@ ParseContext<FullParseHandler>::define(J
         MOZ_ASSUME_UNREACHABLE("unexpected kind");
     }
 
     return true;
 }
 
 template <>
 bool
-ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name, Node pn,
+ParseContext<SyntaxParseHandler>::define(TokenStream &ts, PropertyName *name, Node pn,
                                          Definition::Kind kind)
 {
     JS_ASSERT(!decls_.lookupFirst(name));
 
     if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
         lexdeps->remove(name);
 
     // Keep track of the number of arguments in args_, for fun->nargs.
@@ -291,22 +294,23 @@ AppendPackedBindings(const ParseContext<
                         pc->decls().lookupFirst(name) == dn);
 
         *dst = Binding(name, kind, aliased);
     }
 }
 
 template <typename ParseHandler>
 bool
-ParseContext<ParseHandler>::generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const
+ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, LifoAlloc &alloc,
+                                                     InternalHandle<Bindings*> bindings) const
 {
     JS_ASSERT(sc->isFunctionBox());
 
     unsigned count = args_.length() + vars_.length();
-    Binding *packedBindings = cx->tempLifoAlloc().newArrayUninitialized<Binding>(count);
+    Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count);
     if (!packedBindings) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
     AppendPackedBindings(this, args_, packedBindings);
     AppendPackedBindings(this, vars_, packedBindings + args_.length());
 
@@ -375,89 +379,89 @@ template <>
 bool
 Parser<SyntaxParseHandler>::abortIfSyntaxParser()
 {
     abortedSyntaxParse = true;
     return false;
 }
 
 template <typename ParseHandler>
-Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
+Parser<ParseHandler>::Parser(ExclusiveContext *cx, LifoAlloc *alloc,
+                             const CompileOptions &options,
                              const jschar *chars, size_t length, bool foldConstants,
                              Parser<SyntaxParseHandler> *syntaxParser,
                              LazyScript *lazyOuterFunction)
   : AutoGCRooter(cx, PARSER),
     context(cx),
+    alloc(*alloc),
     tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms),
     traceListHead(NULL),
     pc(NULL),
     sct(NULL),
-    keepAtoms(cx->runtime()),
+    keepAtoms(cx->asJSContext()->runtime()),
     foldConstants(foldConstants),
-    compileAndGo(options.compileAndGo),
-    selfHostingMode(options.selfHostingMode),
     abortedSyntaxParse(false),
-    handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
-{
-    cx->runtime()->activeCompilations++;
+    handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
+{
+    cx->asJSContext()->runtime()->activeCompilations++;
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
-    if (context->hasExtraWarningsOption())
+    if (options.extraWarningsOption)
         handler.disableSyntaxParser();
 
-    tempPoolMark = cx->tempLifoAlloc().mark();
+    tempPoolMark = alloc->mark();
 }
 
 template <typename ParseHandler>
 Parser<ParseHandler>::~Parser()
 {
-    JSContext *cx = context;
-    cx->tempLifoAlloc().release(tempPoolMark);
-    cx->runtime()->activeCompilations--;
+    context->asJSContext()->runtime()->activeCompilations--;
+
+    alloc.release(tempPoolMark);
 
     /*
      * The parser can allocate enormous amounts of memory for large functions.
      * Eagerly free the memory now (which otherwise won't be freed until the
      * next GC) to avoid unnecessary OOMs.
      */
-    cx->tempLifoAlloc().freeAllIfHugeAndUnused();
+    alloc.freeAllIfHugeAndUnused();
 }
 
 template <typename ParseHandler>
 ObjectBox *
 Parser<ParseHandler>::newObjectBox(JSObject *obj)
 {
     JS_ASSERT(obj && !IsPoisonedPtr(obj));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
 
-    ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>(obj, traceListHead);
+    ObjectBox *objbox = alloc.new_<ObjectBox>(obj, traceListHead);
     if (!objbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 template <typename ParseHandler>
-FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun,
-                         ParseContext<ParseHandler> *outerpc, bool strict)
+FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
+                         ParseContext<ParseHandler> *outerpc, bool strict, bool extraWarnings)
   : ObjectBox(fun, traceListHead),
-    SharedContext(cx, strict),
+    SharedContext(cx, strict, extraWarnings),
     bindings(),
     bufStart(0),
     bufEnd(0),
     asmStart(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     useAsm(false),
@@ -521,31 +525,32 @@ Parser<ParseHandler>::newFunctionBox(JSF
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     FunctionBox *funbox =
-        context->tempLifoAlloc().new_<FunctionBox>(context, traceListHead, fun, outerpc, strict);
+        alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
+                                strict, options().extraWarningsOption);
     if (!funbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
     traceListHead = funbox;
 
     return funbox;
 }
 
-ModuleBox::ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module,
-                     ParseContext<FullParseHandler> *pc)
-    : ObjectBox(module, traceListHead),
-      SharedContext(cx, true)
+ModuleBox::ModuleBox(ExclusiveContext *cx, ObjectBox *traceListHead, Module *module,
+                     ParseContext<FullParseHandler> *pc, bool extraWarnings)
+  : ObjectBox(module, traceListHead),
+    SharedContext(cx, true, extraWarnings)
 {
 }
 
 template <>
 ModuleBox *
 Parser<FullParseHandler>::newModuleBox(Module *module, ParseContext<FullParseHandler> *outerpc)
 {
     JS_ASSERT(module && !IsPoisonedPtr(module));
@@ -553,17 +558,18 @@ Parser<FullParseHandler>::newModuleBox(M
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
     ModuleBox *modulebox =
-        context->tempLifoAlloc().new_<ModuleBox>(context, traceListHead, module, outerpc);
+        alloc.new_<ModuleBox>(context, traceListHead, module, outerpc,
+                              options().extraWarningsOption);
     if (!modulebox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
     traceListHead = modulebox;
 
     return modulebox;
@@ -592,17 +598,18 @@ Parser<ParseHandler>::parse(JSObject *ch
     /*
      * Protect atoms from being collected by a GC activation, which might
      * - nest on this thread due to out of memory (the so-called "last ditch"
      *   GC attempted within js_NewGCThing), or
      * - run for any reason on another thread if this thread is suspended on
      *   an object lock before it finishes generating bytecode into a script
      *   protected from the GC by a root or a stack frame reference.
      */
-    GlobalSharedContext globalsc(context, chain, StrictModeFromContext(context));
+    GlobalSharedContext globalsc(context, chain,
+                                 options().strictOption, options().extraWarningsOption);
     ParseContext<ParseHandler> globalpc(this, NULL, &globalsc, /* staticLevel = */ 0, /* bodyid = */ 0);
     if (!globalpc.init())
         return null();
 
     Node pn = statements();
     if (pn) {
         if (!tokenStream.matchToken(TOK_EOF)) {
             report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
@@ -752,17 +759,17 @@ HasFinalReturn(SyntaxParseHandler::Node 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
                                       unsigned errnum, unsigned anonerrnum)
 {
     JSAutoByteString name;
     JSAtom *atom = pc->sc->asFunctionBox()->function()->atom();
     if (atom) {
-        if (!js_AtomToPrintableString(context, atom, &name))
+        if (!AtomToPrintableString(context, atom, &name))
             return false;
     } else {
         errnum = anonerrnum;
     }
     return report(kind, pc->sc->strict, pn, errnum, name.ptr());
 }
 
 template <typename ParseHandler>
@@ -787,17 +794,17 @@ Parser<ParseHandler>::checkStrictAssignm
         return true;
 
     JSAtom *atom = handler.isName(lhs);
     if (!atom)
         return true;
 
     if (atom == context->names().eval || atom == context->names().arguments) {
         JSAutoByteString name;
-        if (!js_AtomToPrintableString(context, atom, &name) ||
+        if (!AtomToPrintableString(context, atom, &name) ||
             !report(ParseStrictError, pc->sc->strict, lhs,
                     JSMSG_DEPRECATED_ASSIGN, name.ptr()))
         {
             return false;
         }
     }
     return true;
 }
@@ -812,17 +819,17 @@ template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkStrictBinding(HandlePropertyName name, Node pn)
 {
     if (!pc->sc->needStrictChecks())
         return true;
 
     if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
         JSAutoByteString bytes;
-        if (!js_AtomToPrintableString(context, name, &bytes))
+        if (!AtomToPrintableString(context, name, &bytes))
             return false;
         return report(ParseStrictError, pc->sc->strict, pn,
                       JSMSG_BAD_BINDING, bytes.ptr());
     }
 
     return true;
 }
 
@@ -860,24 +867,24 @@ Parser<FullParseHandler>::standaloneFunc
         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
         return null();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     InternalHandle<Bindings*> scriptBindings(script, &script->bindings);
-    if (!funpc.generateFunctionBindings(context, scriptBindings))
+    if (!funpc.generateFunctionBindings(context, alloc, scriptBindings))
         return null();
 
     // Also populate the internal bindings of the function box, so that
     // heavyweight tests while emitting bytecode work.
     InternalHandle<Bindings*> funboxBindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&(*funbox)->bindings);
-    if (!funpc.generateFunctionBindings(context, funboxBindings))
+    if (!funpc.generateFunctionBindings(context, alloc, funboxBindings))
         return null();
 
     return pn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkFunctionArguments()
@@ -902,17 +909,17 @@ Parser<FullParseHandler>::checkFunctionA
      * create a declaration for 'arguments' if there are any unbound uses in
      * the function body.
      */
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
         if (r.front().key() == arguments) {
             Definition *dn = r.front().value().get<FullParseHandler>();
             pc->lexdeps->remove(arguments);
             dn->pn_dflags |= PND_IMPLICITARGUMENTS;
-            if (!pc->define(context, arguments, dn, Definition::VAR))
+            if (!pc->define(tokenStream, arguments, dn, Definition::VAR))
                 return false;
             pc->sc->asFunctionBox()->usesArguments = true;
             break;
         }
     }
 
     /*
      * Report error if both rest parameters and 'arguments' are used. Do this
@@ -931,17 +938,17 @@ Parser<FullParseHandler>::checkFunctionA
      * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
      * forces an 'arguments' binding. The exception is that functions with rest
      * parameters are free from 'arguments'.
      */
     if (!argumentsHasBinding && pc->sc->bindingsAccessedDynamically() && !hasRest) {
         ParseNode *pn = newName(arguments);
         if (!pn)
             return false;
-        if (!pc->define(context, arguments, pn, Definition::VAR))
+        if (!pc->define(tokenStream, arguments, pn, Definition::VAR))
             return false;
         argumentsHasBinding = true;
         argumentsHasLocalBinding = true;
     }
 
     /*
      * Now that all possible 'arguments' bindings have been added, note whether
      * 'arguments' has a local binding and whether it unconditionally needs an
@@ -1046,17 +1053,17 @@ Parser<ParseHandler>::functionBody(Funct
             reportBadReturn(pn, ParseError,
                             JSMSG_BAD_GENERATOR_RETURN,
                             JSMSG_BAD_ANON_GENERATOR_RETURN);
             return null();
         }
     }
 
     /* Check for falling off the end of a function that returns a value. */
-    if (context->hasExtraWarningsOption() && pc->funHasReturnExpr && !checkFinalReturn(pn))
+    if (options().extraWarningsOption && pc->funHasReturnExpr && !checkFinalReturn(pn))
         return null();
 
     /* Define the 'arguments' binding if necessary. */
     if (!checkFunctionArguments())
         return null();
 
     return pn;
 }
@@ -1138,29 +1145,29 @@ Parser<FullParseHandler>::makeDefIntoUse
  * simple variable declaration parsers.  In the destructuring case, the binder
  * function is called indirectly from the variable declaration parser by way
  * of CheckDestructuring and its friends.
  */
 
 template <typename ParseHandler>
 struct BindData
 {
-    BindData(JSContext *cx) : let(cx) {}
+    BindData(ExclusiveContext *cx) : let(cx) {}
 
     typedef bool
-    (*Binder)(JSContext *cx, BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser);
+    (*Binder)(BindData *data, HandlePropertyName name, Parser<ParseHandler> *parser);
 
     /* name node for definition processing and error source coordinates */
     typename ParseHandler::Node pn;
 
     JSOp            op;         /* prolog bytecode or nop */
     Binder          binder;     /* binder, discriminates u */
 
     struct LetData {
-        LetData(JSContext *cx) : blockObj(cx) {}
+        LetData(ExclusiveContext *cx) : blockObj(cx) {}
         VarContext varContext;
         RootedStaticBlockObject blockObj;
         unsigned   overflow;
     } let;
 
     void initLet(VarContext varContext, StaticBlockObject &blockObj, unsigned overflow) {
         this->pn = ParseHandler::null();
         this->op = JSOP_NOP;
@@ -1198,41 +1205,41 @@ Parser<ParseHandler>::newFunction(Generi
     RootedFunction fun(context);
     JSFunction::Flags flags = (kind == Expression)
                               ? JSFunction::INTERPRETED_LAMBDA
                               : (kind == Arrow)
                                 ? JSFunction::INTERPRETED_LAMBDA_ARROW
                                 : JSFunction::INTERPRETED;
     fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom,
                       JSFunction::FinalizeKind, MaybeSingletonObject);
-    if (selfHostingMode)
+    if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
-    if (fun && !compileAndGo) {
-        if (!JSObject::clearParent(context, fun))
+    if (fun && !options().compileAndGo) {
+        if (!JSObject::clearParent(context->asJSContext(), fun))
             return NULL;
-        if (!JSObject::clearType(context, fun))
+        if (!JSObject::clearType(context->asJSContext(), fun))
             return NULL;
         fun->setEnvironment(NULL);
     }
     return fun;
 }
 
 static bool
-MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
-{
-    TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
+MatchOrInsertSemicolon(TokenStream &ts)
+{
+    TokenKind tt = ts.peekTokenSameLine(TSF_OPERAND);
     if (tt == TOK_ERROR)
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /* Advance the scanner for proper error location reporting. */
-        ts->getToken(TSF_OPERAND);
-        ts->reportError(JSMSG_SEMI_BEFORE_STMNT);
+        ts.getToken(TSF_OPERAND);
+        ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
-    (void) ts->matchToken(TOK_SEMI);
+    (void) ts.matchToken(TOK_SEMI);
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::DefinitionNode
 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
 {
     AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
@@ -1244,21 +1251,21 @@ Parser<ParseHandler>::getOrCreateLexical
         return ParseHandler::nullDefinition();
     DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
     if (!pc->lexdeps->add(p, atom, def))
         return ParseHandler::nullDefinition();
     return dn;
 }
 
 static bool
-ConvertDefinitionToNamedLambdaUse(JSContext *cx, ParseContext<FullParseHandler> *pc,
+ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler> *pc,
                                   FunctionBox *funbox, Definition *dn)
 {
     dn->setOp(JSOP_CALLEE);
-    if (!dn->pn_cookie.set(cx, pc->staticLevel, UpvarCookie::CALLEE_SLOT))
+    if (!dn->pn_cookie.set(ts, pc->staticLevel, UpvarCookie::CALLEE_SLOT))
         return false;
     dn->pn_dflags |= PND_BOUND;
     JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
 
     /*
      * Since 'dn' is a placeholder, it has not been defined in the
      * ParseContext and hence we must manually flag a closed-over
      * callee name as needing a dynamic scope (this is done for all
@@ -1297,17 +1304,17 @@ Parser<FullParseHandler>::leaveFunction(
     /* Propagate unresolved lexical names up to outerpc->lexdeps. */
     if (pc->lexdeps->count()) {
         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
             Definition *dn = r.front().value().get<FullParseHandler>();
             JS_ASSERT(dn->isPlaceholder());
 
             if (atom == funName && kind == Expression) {
-                if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
+                if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                     return false;
                 continue;
             }
 
             Definition *outer_dn = outerpc->decls().lookupFirst(atom);
 
             /*
              * Make sure to deoptimize lexical dependencies that are polluted
@@ -1375,17 +1382,17 @@ Parser<FullParseHandler>::leaveFunction(
 
             /* Mark the outer dn as escaping. */
             outer_dn->pn_dflags |= PND_CLOSED;
         }
     }
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    return pc->generateFunctionBindings(context, bindings);
+    return pc->generateFunctionBindings(context, alloc, bindings);
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
                                           ParseContext<SyntaxParseHandler> *outerpc,
                                           FunctionSyntaxKind kind)
 {
@@ -1423,17 +1430,17 @@ Parser<ParseHandler>::defineArg(Node fun
         /*
          * Strict-mode disallows duplicate args. We may not know whether we are
          * in strict mode or not (since the function body hasn't been parsed).
          * In such cases, report will queue up the potential error and return
          * 'true'.
          */
         if (sc->needStrictChecks()) {
             JSAutoByteString bytes;
-            if (!js_AtomToPrintableString(context, name, &bytes))
+            if (!AtomToPrintableString(context, name, &bytes))
                 return false;
             if (!report(ParseStrictError, pc->sc->strict, pn,
                         JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
             {
                 return false;
             }
         }
 
@@ -1453,37 +1460,37 @@ Parser<ParseHandler>::defineArg(Node fun
     Node argpn = newName(name);
     if (!argpn)
         return false;
 
     if (!checkStrictBinding(name, argpn))
         return false;
 
     handler.addFunctionArgument(funcpn, argpn);
-    return pc->define(context, name, argpn, Definition::ARG);
+    return pc->define(tokenStream, name, argpn, Definition::ARG);
 }
 
 #if JS_HAS_DESTRUCTURING
 template <typename ParseHandler>
 /* static */ bool
-Parser<ParseHandler>::bindDestructuringArg(JSContext *cx, BindData<ParseHandler> *data,
+Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data,
                                            HandlePropertyName name, Parser<ParseHandler> *parser)
 {
     ParseContext<ParseHandler> *pc = parser->pc;
     JS_ASSERT(pc->sc->isFunctionBox());
 
     if (pc->decls().lookupFirst(name)) {
         parser->report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
         return false;
     }
 
     if (!parser->checkStrictBinding(name, data->pn))
         return false;
 
-    return pc->define(cx, name, data->pn, Definition::VAR);
+    return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
 }
 #endif /* JS_HAS_DESTRUCTURING */
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn,
                                         bool &hasRest)
 {
@@ -1564,17 +1571,17 @@ Parser<ParseHandler>::functionArguments(
                  * anonymous positional parameter into the destructuring
                  * left-hand-side expression and accumulate it in list.
                  */
                 HandlePropertyName name = context->names().empty;
                 Node rhs = newName(name);
                 if (!rhs)
                     return false;
 
-                if (!pc->define(context, name, rhs, Definition::ARG))
+                if (!pc->define(tokenStream, name, rhs, Definition::ARG))
                     return false;
 
                 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
                 if (!item)
                     return false;
                 if (list) {
                     handler.addList(list, item);
                 } else {
@@ -1671,22 +1678,22 @@ Parser<FullParseHandler>::checkFunctionD
         /*
          * Handle redeclaration and optimize cases where we can statically bind the
          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
          */
         if (Definition *dn = pc->decls().lookupFirst(funName)) {
             JS_ASSERT(!dn->isUsed());
             JS_ASSERT(dn->isDefn());
 
-            if (context->hasExtraWarningsOption() || dn->kind() == Definition::CONST) {
+            if (options().extraWarningsOption || dn->kind() == Definition::CONST) {
                 JSAutoByteString name;
                 ParseReportKind reporter = (dn->kind() != Definition::CONST)
                                            ? ParseExtraWarning
                                            : ParseError;
-                if (!js_AtomToPrintableString(context, funName, &name) ||
+                if (!AtomToPrintableString(context, funName, &name) ||
                     !report(reporter, false, NULL, JSMSG_REDECLARED_VAR,
                             Definition::kindString(dn->kind()), name.ptr()))
                 {
                     return false;
                 }
             }
 
             /*
@@ -1715,17 +1722,17 @@ Parser<FullParseHandler>::checkFunctionD
                 fn->pn_body = NULL;
                 fn->pn_cookie.makeFree();
 
                 pc->lexdeps->remove(funName);
                 handler.freeTree(pn);
                 pn = fn;
             }
 
-            if (!pc->define(context, funName, pn, Definition::VAR))
+            if (!pc->define(tokenStream, funName, pn, Definition::VAR))
                 return false;
         }
 
         if (bodyLevel) {
             JS_ASSERT(pn->functionIsHoisted());
             JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
             JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
         } else {
@@ -1860,28 +1867,28 @@ Parser<SyntaxParseHandler>::checkFunctio
     if (kind == Statement) {
         /*
          * Handle redeclaration and optimize cases where we can statically bind the
          * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
          */
         if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
             if (dn == Definition::CONST) {
                 JSAutoByteString name;
-                if (!js_AtomToPrintableString(context, funName, &name) ||
+                if (!AtomToPrintableString(context, funName, &name) ||
                     !report(ParseError, false, null(), JSMSG_REDECLARED_VAR,
                             Definition::kindString(dn), name.ptr()))
                 {
                     return false;
                 }
             }
         } else if (bodyLevel) {
             if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName))
                 pc->lexdeps->remove(funName);
 
-            if (!pc->define(context, funName, *pn, Definition::VAR))
+            if (!pc->define(tokenStream, funName, *pn, Definition::VAR))
                 return false;
         }
 
         if (!bodyLevel && funName == context->names().arguments)
             pc->sc->setBindingsAccessedDynamically();
     }
 
     if (kind == Arrow) {
@@ -2172,24 +2179,24 @@ Parser<FullParseHandler>::standaloneLazy
     RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
 
     if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
         return null();
 
     if (fun->isNamedLambda()) {
         if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
             Definition *dn = p.value().get<FullParseHandler>();
-            if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
+            if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                 return NULL;
         }
     }
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
-    if (!pc->generateFunctionBindings(context, bindings))
+    if (!pc->generateFunctionBindings(context, alloc, bindings))
         return null();
 
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
@@ -2262,29 +2269,29 @@ Parser<ParseHandler>::functionArgsAndBod
             return false;
         }
         funbox->bufEnd = pos().begin + 1;
 #if JS_HAS_EXPR_CLOSURES
     } else {
         if (tokenStream.hadError())
             return false;
         funbox->bufEnd = pos().end;
-        if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
+        if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
             return false;
     }
 #endif
 
     return finishFunctionDefinition(pn, funbox, prelude, body);
 }
 
 template <>
 ParseNode *
 Parser<FullParseHandler>::moduleDecl()
 {
-    JS_ASSERT(tokenStream.currentToken().name() == context->runtime()->atomState.module);
+    JS_ASSERT(tokenStream.currentToken().name() == context->names().module);
     if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
     {
         report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
         return NULL;
     }
 
     ParseNode *pn = CodeNode::create(PNK_MODULE, &handler);
     if (!pn)
@@ -2416,17 +2423,17 @@ Parser<ParseHandler>::maybeParseDirectiv
         //
         // Note that even if the string isn't one we recognize as a directive,
         // the emitter still shouldn't flag it as useless, as it could become a
         // directive in the future. We don't want to interfere with people
         // taking advantage of directive-prologue-enabled features that appear
         // in other browsers first.
         handler.setPrologue(pn);
 
-        if (directive == context->runtime()->atomState.useStrict) {
+        if (directive == context->names().useStrict) {
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc->setExplicitUseStrict();
             if (!pc->sc->strict) {
                 if (pc->sc->isFunctionBox()) {
                     // Request that this function be reparsed as strict.
                     pc->funBecameStrict = true;
                     return false;
@@ -2526,86 +2533,88 @@ Parser<ParseHandler>::condition()
         !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
     {
         return null();
     }
     return pn;
 }
 
 static bool
-MatchLabel(JSContext *cx, TokenStream *ts, MutableHandlePropertyName label)
-{
-    TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
+MatchLabel(TokenStream &ts, MutableHandlePropertyName label)
+{
+    TokenKind tt = ts.peekTokenSameLine(TSF_OPERAND);
     if (tt == TOK_ERROR)
         return false;
     if (tt == TOK_NAME) {
-        (void) ts->getToken();
-        label.set(ts->currentToken().name());
+        (void) ts.getToken();
+        label.set(ts.currentToken().name());
     } else {
         label.set(NULL);
     }
     return true;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom)
 {
     JSAutoByteString name;
-    if (js_AtomToPrintableString(context, atom, &name))
+    if (AtomToPrintableString(context, atom, &name))
         report(ParseError, false, pn, JSMSG_REDECLARED_VAR, isConst ? "const" : "variable", name.ptr());
     return false;
 }
 
 /*
  * Define a let-variable in a block, let-expression, or comprehension scope. pc
  * must already be in such a scope.
  *
  * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
  * property for the new variable on the block object, pc->blockChain;
  * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
  * data->pn in a slot of the block object.
  */
 template <>
 /* static */ bool
-Parser<FullParseHandler>::bindLet(JSContext *cx, BindData<FullParseHandler> *data,
+Parser<FullParseHandler>::bindLet(BindData<FullParseHandler> *data,
                                   HandlePropertyName name, Parser<FullParseHandler> *parser)
 {
     ParseContext<FullParseHandler> *pc = parser->pc;
     ParseNode *pn = data->pn;
     if (!parser->checkStrictBinding(name, pn))
         return false;
 
+    ExclusiveContext *cx = parser->context;
+
     Rooted<StaticBlockObject *> blockObj(cx, data->let.blockObj);
     unsigned blockCount = blockObj->slotCount();
     if (blockCount == JS_BIT(16)) {
         parser->report(ParseError, false, pn, data->let.overflow);
         return false;
     }
 
     /*
      * Assign block-local index to pn->pn_cookie right away, encoding it as an
      * upvar cookie whose skip tells the current static level. The emitter will
      * adjust the node's slot based on its stack depth model -- and, for global
      * and eval code, js::frontend::CompileScript will adjust the slot
      * again to include script->nfixed.
      */
-    if (!pn->pn_cookie.set(parser->context, pc->staticLevel, uint16_t(blockCount)))
+    if (!pn->pn_cookie.set(parser->tokenStream, pc->staticLevel, uint16_t(blockCount)))
         return false;
 
     /*
      * For bindings that are hoisted to the beginning of the block/function,
      * define() right now. Otherwise, delay define until PushLetScope.
      */
     if (data->let.varContext == HoistVars) {
         JS_ASSERT(!pc->atBodyLevel());
         Definition *dn = pc->decls().lookupFirst(name);
         if (dn && dn->pn_blockid == pc->blockid())
             return parser->reportRedeclaration(pn, dn->isConst(), name);
-        if (!pc->define(cx, name, pn, Definition::LET))
+        if (!pc->define(parser->tokenStream, name, pn, Definition::LET))
             return false;
     }
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
@@ -2619,62 +2628,62 @@ Parser<FullParseHandler>::bindLet(JSCont
 
     /* Store pn in the static block object. */
     blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
     return true;
 }
 
 template <>
 /* static */ bool
-Parser<SyntaxParseHandler>::bindLet(JSContext *cx, BindData<SyntaxParseHandler> *data,
+Parser<SyntaxParseHandler>::bindLet(BindData<SyntaxParseHandler> *data,
                                     HandlePropertyName name, Parser<SyntaxParseHandler> *parser)
 {
     return true;
 }
 
 template <typename ParseHandler, class Op>
 static inline bool
-ForEachLetDef(JSContext *cx, ParseContext<ParseHandler> *pc,
+ForEachLetDef(TokenStream &ts, ParseContext<ParseHandler> *pc,
               HandleStaticBlockObject blockObj, Op op)
 {
-    for (Shape::Range<CanGC> r(cx, blockObj->lastProperty()); !r.empty(); r.popFront()) {
+    for (Shape::Range<CanGC> r(ts.context(), blockObj->lastProperty()); !r.empty(); r.popFront()) {
         Shape &shape = r.front();
 
         /* Beware the destructuring dummy slots. */
         if (JSID_IS_INT(shape.propid()))
             continue;
 
-        if (!op(cx, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
+        if (!op(ts, pc, blockObj, shape, JSID_TO_ATOM(shape.propid())))
             return false;
     }
     return true;
 }
 
 template <typename ParseHandler>
 struct PopLetDecl {
-    bool operator()(JSContext *, ParseContext<ParseHandler> *pc, HandleStaticBlockObject,
+    bool operator()(TokenStream &, ParseContext<ParseHandler> *pc, HandleStaticBlockObject,
                     const Shape &, JSAtom *atom)
     {
         pc->popLetDecl(atom);
         return true;
     }
 };
 
 template <typename ParseHandler>
 static void
-PopStatementPC(JSContext *cx, ParseContext<ParseHandler> *pc)
-{
-    RootedStaticBlockObject blockObj(cx, pc->topStmt->blockObj);
+PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
+{
+    RootedStaticBlockObject blockObj(ts.context(), pc->topStmt->blockObj);
     JS_ASSERT(!!blockObj == (pc->topStmt->isBlockScope));
 
     FinishPopStatement(pc);
 
     if (blockObj) {
         JS_ASSERT(!blockObj->inDictionaryMode());
-        ForEachLetDef(cx, pc, blockObj, PopLetDecl<ParseHandler>());
+        ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
         blockObj->resetPrevBlockChainFromParser();
     }
 }
 
 /*
  * The function LexicalLookup searches a static binding for the given name in
  * the stack of statements enclosing the statement currently being parsed. Each
  * statement that introduces a new scope has a corresponding scope object, on
@@ -2730,19 +2739,20 @@ OuterLet(ParseContext<ParseHandler> *pc,
         if (stmt->type == STMT_BLOCK)
             return true;
     }
     return false;
 }
 
 template <typename ParseHandler>
 /* static */ bool
-Parser<ParseHandler>::bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data,
+Parser<ParseHandler>::bindVarOrConst(BindData<ParseHandler> *data,
                                      HandlePropertyName name, Parser<ParseHandler> *parser)
 {
+    ExclusiveContext *cx = parser->context;
     ParseContext<ParseHandler> *pc = parser->pc;
     Node pn = data->pn;
     bool isConstDecl = data->op == JSOP_DEFCONST;
 
     /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
     parser->handler.setOp(pn, JSOP_NAME);
 
     if (!parser->checkStrictBinding(name, pn))
@@ -2767,52 +2777,54 @@ Parser<ParseHandler>::bindVarOrConst(JSC
                 funbox->setHasDebuggerStatement();
         }
         return true;
     }
 
     DefinitionList::Range defs = pc->decls().lookupMulti(name);
     JS_ASSERT_IF(stmt, !defs.empty());
 
-    if (defs.empty())
-        return pc->define(cx, name, pn, isConstDecl ? Definition::CONST : Definition::VAR);
+    if (defs.empty()) {
+        return pc->define(parser->tokenStream, name, pn,
+                          isConstDecl ? Definition::CONST : Definition::VAR);
+    }
 
     /*
      * There was a previous declaration with the same name. The standard
      * disallows several forms of redeclaration. Critically,
      *   let (x) { var x; } // error
      * is not allowed which allows us to turn any non-error redeclaration
      * into a use of the initial declaration.
      */
     DefinitionNode dn = defs.front<ParseHandler>();
     Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn);
     if (dn_kind == Definition::ARG) {
         JSAutoByteString bytes;
-        if (!js_AtomToPrintableString(cx, name, &bytes))
+        if (!AtomToPrintableString(cx, name, &bytes))
             return false;
 
         if (isConstDecl) {
             parser->report(ParseError, false, pn, JSMSG_REDECLARED_PARAM, bytes.ptr());
             return false;
         }
         if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
             return false;
     } else {
         bool error = (isConstDecl ||
                       dn_kind == Definition::CONST ||
                       (dn_kind == Definition::LET &&
                        (stmt->type != STMT_CATCH || OuterLet(pc, stmt, name))));
 
-        if (cx->hasExtraWarningsOption()
+        if (parser->options().extraWarningsOption
             ? data->op != JSOP_DEFVAR || dn_kind != Definition::VAR
             : error)
         {
             JSAutoByteString bytes;
             ParseReportKind reporter = error ? ParseError : ParseExtraWarning;
-            if (!js_AtomToPrintableString(cx, name, &bytes) ||
+            if (!AtomToPrintableString(cx, name, &bytes) ||
                 !parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
                                 Definition::kindString(dn_kind), bytes.ptr()))
             {
                 return false;
             }
         }
     }
 
@@ -2874,17 +2886,17 @@ template <>
 bool
 Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
 {
     JS_ASSERT(pn->isKind(PNK_NAME));
 
     RootedPropertyName name(context, pn->pn_atom->asPropertyName());
 
     data->pn = pn;
-    if (!data->binder(context, data, name, this))
+    if (!data->binder(data, name, this))
         return false;
 
     /*
      * Select the appropriate name-setting opcode, respecting eager selection
      * done by the data->binder function.
      */
     if (pn->pn_dflags & PND_BOUND)
         pn->setOp(JSOP_SETLOCAL);
@@ -3159,40 +3171,40 @@ Parser<ParseHandler>::pushLexicalScope(S
 #if JS_HAS_BLOCK_SCOPE
 
 struct AddLetDecl
 {
     uint32_t blockid;
 
     AddLetDecl(uint32_t blockid) : blockid(blockid) {}
 
-    bool operator()(JSContext *cx, ParseContext<FullParseHandler> *pc,
+    bool operator()(TokenStream &ts, ParseContext<FullParseHandler> *pc,
                     HandleStaticBlockObject blockObj, const Shape &shape, JSAtom *)
     {
         ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate();
         def->pn_blockid = blockid;
-        RootedPropertyName name(cx, def->name());
-        return pc->define(cx, name, def, Definition::LET);
+        RootedPropertyName name(ts.context(), def->name());
+        return pc->define(ts, name, def, Definition::LET);
     }
 };
 
 template <>
 ParseNode *
 Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
 {
     JS_ASSERT(blockObj);
     ParseNode *pn = pushLexicalScope(blockObj, stmt);
     if (!pn)
         return null();
 
     /* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */
     pn->pn_dflags |= PND_LET;
 
     /* Populate the new scope with decls found in the head with updated blockid. */
-    if (!ForEachLetDef(context, pc, blockObj, AddLetDecl(stmt->blockid)))
+    if (!ForEachLetDef(tokenStream, pc, blockObj, AddLetDecl(stmt->blockid)))
         return null();
 
     return pn;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
@@ -3269,22 +3281,22 @@ Parser<ParseHandler>::letBlock(LetContex
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
     } else {
         JS_ASSERT(letContext == LetExpresion);
         expr = assignExpr();
         if (!expr)
             return null();
     }
     handler.setLeaveBlockResult(block, expr, letContext != LetStatement);
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     handler.setEndPosition(pnlet, pos().end);
 
     if (needExprStmt) {
-        if (!MatchOrInsertSemicolon(context, &tokenStream))
+        if (!MatchOrInsertSemicolon(tokenStream))
             return null();
         return handler.newExprStatement(pnlet, pos().end);
     }
     return pnlet;
 }
 
 #endif /* JS_HAS_BLOCK_SCOPE */
 
@@ -3306,17 +3318,17 @@ Parser<ParseHandler>::blockStatement()
     if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
         return null();
 
     Node list = statements();
     if (!list)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
     return list;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext)
 {
     /*
@@ -3438,17 +3450,17 @@ Parser<ParseHandler>::variables(ParseNod
 
         RootedPropertyName name(context, tokenStream.currentToken().name());
         pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
         if (!pn2)
             return null();
         if (data.op == JSOP_DEFCONST)
             handler.setFlag(pn2, PND_CONST);
         data.pn = pn2;
-        if (!data.binder(context, &data, name, this))
+        if (!data.binder(&data, name, this))
             return null();
         handler.addList(pn, pn2);
 
         if (tokenStream.matchToken(TOK_ASSIGN)) {
             if (psimple)
                 *psimple = false;
 
             Node init = assignExpr();
@@ -3569,17 +3581,17 @@ Parser<FullParseHandler>::letStatement()
 
         pn = variables(PNK_LET, NULL, pc->blockChain, HoistVars);
         if (!pn)
             return null();
         pn->pn_xflags = PNX_POPVAR;
     } while (0);
 
     /* Check termination of this primitive statement. */
-    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
+    return MatchOrInsertSemicolon(tokenStream) ? pn : NULL;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::letStatement()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
@@ -3590,17 +3602,17 @@ Parser<SyntaxParseHandler>::letStatement
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement()
 {
     tokenStream.ungetToken();
     Node pnexpr = expr();
     if (!pnexpr)
         return null();
-    if (!MatchOrInsertSemicolon(context, &tokenStream))
+    if (!MatchOrInsertSemicolon(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::ifStatement()
 {
@@ -3628,17 +3640,17 @@ Parser<ParseHandler>::ifStatement()
         stmtInfo.type = STMT_ELSE;
         elseBranch = statement();
         if (!elseBranch)
             return null();
     } else {
         elseBranch = null();
     }
 
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
     return handler.newIfStatement(begin, cond, thenBranch, elseBranch);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::doWhileStatement()
 {
     uint32_t begin = pos().begin;
@@ -3646,22 +3658,22 @@ Parser<ParseHandler>::doWhileStatement()
     PushStatementPC(pc, &stmtInfo, STMT_DO_LOOP);
     Node body = statement();
     if (!body)
         return null();
     MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
     Node cond = condition();
     if (!cond)
         return null();
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     if (versionNumber() == JSVERSION_ECMA_3) {
         // Pedantically require a semicolon or line break, following ES3.
         // Bug 880329 proposes removing this case.
-        if (!MatchOrInsertSemicolon(context, &tokenStream))
+        if (!MatchOrInsertSemicolon(tokenStream))
             return null();
     } else {
         // The semicolon after do-while is even more optional than most
         // semicolons in JS.  Web compat required this by 2004:
         //   http://bugzilla.mozilla.org/show_bug.cgi?id=238945
         // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
         //   https://bugs.ecmascript.org/show_bug.cgi?id=157
         (void) tokenStream.matchToken(TOK_SEMI);
@@ -3678,17 +3690,17 @@ Parser<ParseHandler>::whileStatement()
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_WHILE_LOOP);
     Node cond = condition();
     if (!cond)
         return null();
     Node body = statement();
     if (!body)
         return null();
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
 {
     if (tokenStream.matchToken(TOK_IN)) {
@@ -4044,19 +4056,19 @@ Parser<FullParseHandler>::forStatement()
 
     /* Parse the loop body. */
     ParseNode *body = statement();
     if (!body)
         return null();
 
 #if JS_HAS_BLOCK_SCOPE
     if (blockObj)
-        PopStatementPC(context, pc);
+        PopStatementPC(tokenStream, pc);
 #endif
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     ParseNode *forLoop = handler.newForStatement(begin, forHead, body, iflags);
     if (!forLoop)
         return null();
 
     if (hoistedVar) {
         ParseNode *pnseq = handler.newList(PNK_SEQ, hoistedVar);
         if (!pnseq)
@@ -4182,17 +4194,17 @@ Parser<SyntaxParseHandler>::forStatement
     }
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
     if (!statement())
         return null();
 
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
     return SyntaxParseHandler::NodeGeneric;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::switchStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
@@ -4277,32 +4289,32 @@ Parser<ParseHandler>::switchStatement()
      * the switch body, but not within an inner block.  If it replaced
      * pc->blockNode with a new block node then we must refresh caseList and
      * then restore pc->blockNode.
      */
     if (pc->blockNode != caseList)
         caseList = pc->blockNode;
     pc->blockNode = saveBlock;
 
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     handler.setEndPosition(caseList, pos().end);
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::continueStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
-    if (!MatchLabel(context, &tokenStream, &label))
+    if (!MatchLabel(tokenStream, &label))
         return null();
 
     StmtInfoPC *stmt = pc->topStmt;
     if (label) {
         for (StmtInfoPC *stmt2 = NULL; ; stmt = stmt->down) {
             if (!stmt) {
                 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
                 return null();
@@ -4325,31 +4337,31 @@ Parser<ParseHandler>::continueStatement(
                 report(ParseError, false, null(), JSMSG_BAD_CONTINUE);
                 return null();
             }
             if (stmt->isLoop())
                 break;
         }
     }
 
-    if (!MatchOrInsertSemicolon(context, &tokenStream))
+    if (!MatchOrInsertSemicolon(tokenStream))
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::breakStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
-    if (!MatchLabel(context, &tokenStream, &label))
+    if (!MatchLabel(tokenStream, &label))
         return null();
     StmtInfoPC *stmt = pc->topStmt;
     if (label) {
         for (; ; stmt = stmt->down) {
             if (!stmt) {
                 report(ParseError, false, null(), JSMSG_LABEL_NOT_FOUND);
                 return null();
             }
@@ -4362,17 +4374,17 @@ Parser<ParseHandler>::breakStatement()
                 report(ParseError, false, null(), JSMSG_TOUGH_BREAK);
                 return null();
             }
             if (stmt->isLoop() || stmt->type == STMT_SWITCH)
                 break;
         }
     }
 
-    if (!MatchOrInsertSemicolon(context, &tokenStream))
+    if (!MatchOrInsertSemicolon(tokenStream))
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::returnStatementOrYieldExpression()
@@ -4430,34 +4442,34 @@ Parser<ParseHandler>::returnStatementOrY
         exprNode = isYield ? assignExpr() : expr();
         if (!exprNode)
             return null();
         if (!isYield)
             pc->funHasReturnExpr = true;
     }
 
     if (!isYield) {
-        if (!MatchOrInsertSemicolon(context, &tokenStream))
+        if (!MatchOrInsertSemicolon(tokenStream))
             return null();
     }
 
     Node pn = isYield
               ? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode)
               : handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
     if (!pn)
         return null();
 
     if (pc->funHasReturnExpr && pc->sc->asFunctionBox()->isGenerator()) {
         /* As in Python (see PEP-255), disallow return v; in generators. */
         reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
                         JSMSG_BAD_ANON_GENERATOR_RETURN);
         return null();
     }
 
-    if (context->hasExtraWarningsOption() && pc->funHasReturnExpr && pc->funHasReturnVoid &&
+    if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
         !reportBadReturn(pn, ParseExtraWarning,
                          JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
     {
         return null();
     }
 
     return pn;
 }
@@ -4493,17 +4505,17 @@ Parser<FullParseHandler>::withStatement(
     bool oldParsingWith = pc->parsingWith;
     pc->parsingWith = true;
 
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_WITH);
     Node innerBlock = statement();
     if (!innerBlock)
         return null();
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     pc->sc->setBindingsAccessedDynamically();
     pc->parsingWith = oldParsingWith;
 
     /*
      * Make sure to deoptimize lexical dependencies inside the |with|
      * to safely optimize binding globals (see bug 561923).
      */
@@ -4543,17 +4555,17 @@ Parser<ParseHandler>::labeledStatement()
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_LABEL);
     stmtInfo.label = label;
     Node pn = statement();
     if (!pn)
         return null();
 
     /* Pop the label, set pn_expr, and return early. */
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::throwStatement()
 {
@@ -4568,17 +4580,17 @@ Parser<ParseHandler>::throwStatement()
         report(ParseError, false, null(), JSMSG_SYNTAX_ERROR);
         return null();
     }
 
     Node throwExpr = expr();
     if (!throwExpr)
         return null();
 
-    if (!MatchOrInsertSemicolon(context, &tokenStream))
+    if (!MatchOrInsertSemicolon(tokenStream))
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::tryStatement()
@@ -4607,17 +4619,17 @@ Parser<ParseHandler>::tryStatement()
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
     StmtInfoPC stmtInfo(context);
     if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, pc))
         return null();
     Node innerBlock = statements();
     if (!innerBlock)
         return null();
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
     TokenKind tt = tokenStream.getToken();
     if (tt == TOK_CATCH) {
         catchList = handler.newList(PNK_CATCH);
         if (!catchList)
             return null();
@@ -4672,17 +4684,17 @@ Parser<ParseHandler>::tryStatement()
 
               case TOK_NAME:
               {
                 RootedPropertyName label(context, tokenStream.currentToken().name());
                 catchName = newBindingNode(label, false);
                 if (!catchName)
                     return null();
                 data.pn = catchName;
-                if (!data.binder(context, &data, label, this))
+                if (!data.binder(&data, label, this))
                     return null();
                 break;
               }
 
               default:
                 report(ParseError, false, null(), JSMSG_CATCH_IDENTIFIER);
                 return null();
             }
@@ -4702,17 +4714,17 @@ Parser<ParseHandler>::tryStatement()
 #endif
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
             Node catchBody = statements();
             if (!catchBody)
                 return null();
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
-            PopStatementPC(context, pc);
+            PopStatementPC(tokenStream, pc);
 
             if (!catchGuard)
                 hasUnconditionalCatch = true;
 
             if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
                 return null();
             handler.setEndPosition(catchList, pos().end);
             handler.setEndPosition(pnblock, pos().end);
@@ -4726,17 +4738,17 @@ Parser<ParseHandler>::tryStatement()
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
         if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, pc))
             return null();
         finallyBlock = statements();
         if (!finallyBlock)
             return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
-        PopStatementPC(context, pc);
+        PopStatementPC(tokenStream, pc);
     } else {
         tokenStream.ungetToken();
     }
     if (!catchList && !finallyBlock) {
         report(ParseError, false, null(), JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
@@ -4744,17 +4756,17 @@ Parser<ParseHandler>::tryStatement()
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
-    if (!MatchOrInsertSemicolon(context, &tokenStream))
+    if (!MatchOrInsertSemicolon(tokenStream))
         return null();
     p.end = pos().end;
 
     pc->sc->setBindingsAccessedDynamically();
     pc->sc->setHasDebuggerStatement();
 
     return handler.newDebuggerStatement(p);
 }
@@ -4855,17 +4867,17 @@ Parser<ParseHandler>::statement(bool can
         }
         return expressionStatement();
 
       default:
         return expressionStatement();
     }
 
     /* Check termination of this primitive statement. */
-    return MatchOrInsertSemicolon(context, &tokenStream) ? pn : null();
+    return MatchOrInsertSemicolon(tokenStream) ? pn : null();
 }
 
 template <>
 ParseNode *
 Parser<FullParseHandler>::expr()
 {
     ParseNode *pn = assignExpr();
     if (pn && tokenStream.matchToken(TOK_COMMA)) {
@@ -5509,33 +5521,33 @@ GenexpGuard<ParseHandler>::maybeNoteGene
 
 /*
  * Any definitions nested within the comprehension expression of a generator
  * expression must move "down" one static level, which of course increases the
  * upvar-frame-skip count.
  */
 template <typename ParseHandler>
 static bool
-BumpStaticLevel(ParseNode *pn, ParseContext<ParseHandler> *pc)
+BumpStaticLevel(TokenStream &ts, ParseNode *pn, ParseContext<ParseHandler> *pc)
 {
     if (pn->pn_cookie.isFree())
         return true;
 
     unsigned level = unsigned(pn->pn_cookie.level()) + 1;
     JS_ASSERT(level >= pc->staticLevel);
-    return pn->pn_cookie.set(pc->sc->context, level, pn->pn_cookie.slot());
+    return pn->pn_cookie.set(ts, level, pn->pn_cookie.slot());
 }
 
 template <typename ParseHandler>
 static bool
-AdjustBlockId(ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc)
+AdjustBlockId(TokenStream &ts, ParseNode *pn, unsigned adjust, ParseContext<ParseHandler> *pc)
 {
     JS_ASSERT(pn->isArity(PN_LIST) || pn->isArity(PN_CODE) || pn->isArity(PN_NAME));
     if (JS_BIT(20) - pn->pn_blockid <= adjust + 1) {
-        JS_ReportErrorNumber(pc->sc->context, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, "program");
+        ts.reportError(JSMSG_NEED_DIET, "program");
         return false;
     }
     pn->pn_blockid += adjust;
     if (pn->pn_blockid >= pc->blockidGen)
         pc->blockidGen = pn->pn_blockid + 1;
     return true;
 }
 
@@ -5549,17 +5561,17 @@ CompExprTransplanter::transplant(ParseNo
 
     switch (pn->getArity()) {
       case PN_LIST:
         for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
             if (!transplant(pn2))
                 return false;
         }
         if (pn->pn_pos >= root->pn_pos) {
-            if (!AdjustBlockId(pn, adjust, pc))
+            if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
                 return false;
         }
         break;
 
       case PN_TERNARY:
         if (!transplant(pn->pn_kid1) ||
             !transplant(pn->pn_kid2) ||
             !transplant(pn->pn_kid3))
@@ -5583,17 +5595,17 @@ CompExprTransplanter::transplant(ParseNo
         break;
 
       case PN_CODE:
       case PN_NAME:
         if (!transplant(pn->maybeExpr()))
             return false;
 
         if (pn->isDefn()) {
-            if (genexp && !BumpStaticLevel(pn, pc))
+            if (genexp && !BumpStaticLevel(parser->tokenStream, pn, pc))
                 return false;
         } else if (pn->isUsed()) {
             JS_ASSERT(pn->pn_cookie.isFree());
 
             Definition *dn = pn->pn_lexdef;
             JS_ASSERT(dn->isDefn());
 
             /*
@@ -5601,19 +5613,19 @@ CompExprTransplanter::transplant(ParseNo
              * to the left of the root node, and if pn is the last use visited
              * in the comprehension expression (to avoid adjusting the blockid
              * multiple times).
              *
              * Non-placeholder definitions within the comprehension expression
              * will be visited further below.
              */
             if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
-                if (genexp && !BumpStaticLevel(dn, pc))
+                if (genexp && !BumpStaticLevel(parser->tokenStream, dn, pc))
                     return false;
-                if (!AdjustBlockId(dn, adjust, pc))
+                if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
                     return false;
             }
 
             RootedAtom atom(parser->context, pn->pn_atom);
 #ifdef DEBUG
             StmtInfoPC *stmt = LexicalLookup(pc, atom, NULL, (StmtInfoPC *)NULL);
             JS_ASSERT(!stmt || stmt != pc->topStmt);
 #endif
@@ -5668,29 +5680,29 @@ CompExprTransplanter::transplant(ParseNo
                     /*
                      * Implicit 'arguments' Definition nodes (see
                      * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
                      * reachable via the lexdefs of their uses. Unfortunately,
                      * there may be multiple uses, so we need to maintain a set
                      * to only bump the definition once.
                      */
                     if (genexp && !visitedImplicitArguments.has(dn)) {
-                        if (!BumpStaticLevel(dn, pc))
+                        if (!BumpStaticLevel(parser->tokenStream, dn, pc))
                             return false;
-                        if (!AdjustBlockId(dn, adjust, pc))
+                        if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
                             return false;
                         if (!visitedImplicitArguments.put(dn))
                             return false;
                     }
                 }
             }
         }
 
         if (pn->pn_pos >= root->pn_pos) {
-            if (!AdjustBlockId(pn, adjust, pc))
+            if (!AdjustBlockId(parser->tokenStream, pn, adjust, pc))
                 return false;
         }
         break;
 
       case PN_NULLARY:
         /* Nothing. */
         break;
     }
@@ -5886,17 +5898,17 @@ Parser<FullParseHandler>::comprehensionT
                 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
                 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
             }
             break;
 #endif
 
           case TOK_NAME:
             data.pn = pn3;
-            if (!data.binder(context, &data, name, this))
+            if (!data.binder(&data, name, this))
                 return null();
             break;
 
           default:;
         }
 
         /*
          * Synthesize a declaration. Every definition must appear in the parse
@@ -5935,17 +5947,17 @@ Parser<FullParseHandler>::comprehensionT
 
     pn2 = UnaryNode::create(kind, &handler);
     if (!pn2)
         return null();
     pn2->setOp(op);
     pn2->pn_kid = kid;
     *pnp = pn2;
 
-    PopStatementPC(context, pc);
+    PopStatementPC(tokenStream, pc);
     return pn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::arrayInitializerComprehensionTail(ParseNode *pn)
 {
     /* Relabel pn as an array comprehension node. */
@@ -6655,17 +6667,17 @@ Parser<ParseHandler>::primaryExpr(TokenK
             AtomIndexAddPtr p = seen.lookupForAdd(atom);
             if (p) {
                 jsatomid index = p.value();
                 AssignmentType oldAssignType = AssignmentType(index);
                 if ((oldAssignType & assignType) &&
                     (oldAssignType != VALUE || assignType != VALUE || pc->sc->needStrictChecks()))
                 {
                     JSAutoByteString name;
-                    if (!js_AtomToPrintableString(context, atom, &name))
+                    if (!AtomToPrintableString(context, atom, &name))
                         return null();
 
                     ParseReportKind reportKind =
                         (oldAssignType == VALUE && assignType == VALUE && !pc->sc->needStrictChecks())
                         ? ParseWarning
                         : (pc->sc->needStrictChecks() ? ParseStrictError : ParseError);
                     if (!report(reportKind, pc->sc->strict, null(),
                                 JSMSG_DUPLICATE_PROPERTY, name.ptr()))
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -24,17 +24,17 @@ namespace js {
 namespace frontend {
 
 struct StmtInfoPC : public StmtInfoBase {
     StmtInfoPC      *down;          /* info for enclosing statement */
     StmtInfoPC      *downScope;     /* next enclosing lexical scope */
 
     uint32_t        blockid;        /* for simplified dominance computation */
 
-    StmtInfoPC(JSContext *cx) : StmtInfoBase(cx) {}
+    StmtInfoPC(ExclusiveContext *cx) : StmtInfoBase(cx) {}
 };
 
 typedef HashSet<JSAtom *> FuncStmtSet;
 class SharedContext;
 
 typedef Vector<Definition *, 16> DeclVector;
 
 struct GenericParseContext
@@ -150,17 +150,17 @@ struct ParseContext : public GenericPars
      *    non-placeholder definition.
      *  + If this is a function scope (sc->inFunction), 'pn' is bound to a
      *    particular local/argument slot.
      *  + PND_CONST is set for Definition::COSNT
      *  + Pre-existing uses of pre-existing placeholders have been linked to
      *    'pn' if they are in the scope of 'pn'.
      *  + Pre-existing placeholders in the scope of 'pn' have been removed.
      */
-    bool define(JSContext *cx, HandlePropertyName name, Node pn, Definition::Kind);
+    bool define(TokenStream &ts, PropertyName *name, Node pn, Definition::Kind);
 
     /*
      * Let definitions may shadow same-named definitions in enclosing scopes.
      * To represesent this, 'decls' is not a plain map, but actually:
      *   decls :: name -> stack of definitions
      * New bindings are pushed onto the stack, name lookup always refers to the
      * top of the stack, and leaving a block scope calls popLetDecl for each
      * name in the block's scope.
@@ -182,17 +182,18 @@ struct ParseContext : public GenericPars
      *    the use/def nodes, but the emitter occasionally needs 'bindings' for
      *    various scope-related queries.
      *  - Bindings provide the initial js::Shape to use when creating a dynamic
      *    scope object (js::CallObject) for the function. This shape is used
      *    during dynamic name lookup.
      *  - Sometimes a script's bindings are accessed at runtime to retrieve the
      *    contents of the lexical scope (e.g., from the debugger).
      */
-    bool generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const;
+    bool generateFunctionBindings(ExclusiveContext *cx, LifoAlloc &alloc,
+                                  InternalHandle<Bindings*> bindings) const;
 
   public:
     uint32_t         yieldOffset;   /* offset of a yield expression that might
                                        be an error if we turn out to be inside
                                        a generator expression.  Zero means
                                        there isn't one. */
   private:
     ParseContext    **parserPC;     /* this points to the Parser's active pc
@@ -235,17 +236,17 @@ struct ParseContext : public GenericPars
         blockidGen(bodyid),  // used to set |bodyid| and subsequently incremented in init()
         topStmt(NULL),
         topScopeStmt(NULL),
         blockChain(prs->context),
         staticLevel(staticLevel),
         parenDepth(0),
         yieldCount(0),
         blockNode(ParseHandler::null()),
-        decls_(prs->context),
+        decls_(prs->context, prs->alloc),
         args_(prs->context),
         vars_(prs->context),
         yieldOffset(0),
         parserPC(&prs->pc),
         oldpc(prs->pc),
         lexdeps(prs->context),
         funcStmts(NULL),
         innerFunctions(prs->context),
@@ -299,17 +300,19 @@ class GenexpGuard;
 enum LetContext { LetExpresion, LetStatement };
 enum VarContext { HoistVars, DontHoistVars };
 enum FunctionType { Getter, Setter, Normal };
 
 template <typename ParseHandler>
 class Parser : private AutoGCRooter, public StrictModeGetter
 {
   public:
-    JSContext           *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
+    ExclusiveContext *const context;
+    LifoAlloc &alloc;
+
     TokenStream         tokenStream;
     LifoAlloc::Mark     tempPoolMark;
 
     /* list of parsed objects for GC tracing */
     ObjectBox *traceListHead;
 
     /* innermost parse context (stack-allocated) */
     ParseContext<ParseHandler> *pc;
@@ -318,35 +321,16 @@ class Parser : private AutoGCRooter, pub
 
     /* Root atoms and objects allocated for the parsed tree. */
     AutoKeepAtoms       keepAtoms;
 
     /* Perform constant-folding; must be true when interfacing with the emitter. */
     const bool          foldConstants:1;
 
   private:
-    /* Script can optimize name references based on scope chain. */
-    const bool          compileAndGo:1;
-
-    /*
-     * In self-hosting mode, scripts emit JSOP_CALLINTRINSIC instead of
-     * JSOP_NAME or JSOP_GNAME to access unbound variables. JSOP_CALLINTRINSIC
-     * does a name lookup in a special object that contains properties
-     * installed during global initialization and that properties from
-     * self-hosted scripts get copied into lazily upon first access in a
-     * global.
-     * As that object is inaccessible to client code, the lookups are
-     * guaranteed to return the original objects, ensuring safe implementation
-     * of self-hosted builtins.
-     * Additionally, the special syntax _CallFunction(receiver, ...args, fun)
-     * is supported, for which bytecode is emitted that invokes |fun| with
-     * |receiver| as the this-object and ...args as the arguments..
-     */
-    const bool          selfHostingMode:1;
-
     /*
      * Not all language constructs can be handled during syntax parsing. If it
      * is not known whether the parse succeeds or fails, this bit is set and
      * the parse will return false.
      */
     bool abortedSyntaxParse;
 
     typedef typename ParseHandler::Node Node;
@@ -359,17 +343,17 @@ class Parser : private AutoGCRooter, pub
   private:
     bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
                       unsigned errorNumber, va_list args);
   public:
     bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
     bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
                           ...);
 
-    Parser(JSContext *cx, const CompileOptions &options,
+    Parser(ExclusiveContext *cx, LifoAlloc *alloc, const CompileOptions &options,
            const jschar *chars, size_t length, bool foldConstants,
            Parser<SyntaxParseHandler> *syntaxParser,
            LazyScript *lazyOuterFunction);
     ~Parser();
 
     friend void js::frontend::MarkParser(JSTracer *trc, AutoGCRooter *parser);
 
     const char *getFilename() const { return tokenStream.getFilename(); }
@@ -434,16 +418,20 @@ class Parser : private AutoGCRooter, pub
     Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
 
     bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
                                     HandlePropertyName funName, FunctionType type,
                                     FunctionSyntaxKind kind, bool strict, bool *becameStrict);
 
     virtual bool strictMode() { return pc->sc->strict; }
 
+    const CompileOptions &options() const {
+        return tokenStream.options();
+    }
+
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
      * Each returns a parse node tree or null on error.
@@ -558,25 +546,25 @@ class Parser : private AutoGCRooter, pub
     Node cloneLeftHandSide(Node opn);
     Node cloneParseTree(Node opn);
 
     Node newNumber(const Token &tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
     static bool
-    bindDestructuringArg(JSContext *cx, BindData<ParseHandler> *data,
+    bindDestructuringArg(BindData<ParseHandler> *data,
                          HandlePropertyName name, Parser<ParseHandler> *parser);
 
     static bool
-    bindLet(JSContext *cx, BindData<ParseHandler> *data,
+    bindLet(BindData<ParseHandler> *data,
             HandlePropertyName name, Parser<ParseHandler> *parser);
 
     static bool
-    bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data,
+    bindVarOrConst(BindData<ParseHandler> *data,
                    HandlePropertyName name, Parser<ParseHandler> *parser);
 
     static Node null() { return ParseHandler::null(); }
 
     bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
     bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
     bool checkFinalReturn(Node pn);
     DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
--- a/js/src/frontend/SharedContext-inl.h
+++ b/js/src/frontend/SharedContext-inl.h
@@ -11,15 +11,15 @@
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
 inline bool
 SharedContext::needStrictChecks()
 {
-    return context->hasExtraWarningsOption() || strict;
+    return strict || extraWarnings;
 }
 
 } /* namespace frontend */
 } // namespace js
 
 #endif /* frontend_SharedContext_inl_h */
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -144,26 +144,28 @@ class GlobalSharedContext;
  * The struct SharedContext is part of the current parser context (see
  * ParseContext). It stores information that is reused between the parser and
  * the bytecode emitter. Note however, that this information is not shared
  * between the two; they simply reuse the same data structure.
  */
 class SharedContext
 {
   public:
-    JSContext *const context;
+    ExclusiveContext *const context;
     AnyContextFlags anyCxFlags;
     bool strict;
+    bool extraWarnings;
 
     // If it's function code, funbox must be non-NULL and scopeChain must be NULL.
     // If it's global code, funbox must be NULL.
-    SharedContext(JSContext *cx, bool strict)
+    SharedContext(ExclusiveContext *cx, bool strict, bool extraWarnings)
       : context(cx),
         anyCxFlags(),
-        strict(strict)
+        strict(strict),
+        extraWarnings(extraWarnings)
     {}
 
     virtual ObjectBox *toObjectBox() = 0;
     inline bool isGlobalSharedContext() { return toObjectBox() == NULL; }
     inline bool isModuleBox() { return toObjectBox() && toObjectBox()->isModuleBox(); }
     inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
     inline GlobalSharedContext *asGlobalSharedContext();
     inline ModuleBox *asModuleBox();
@@ -182,18 +184,19 @@ class SharedContext
 };
 
 class GlobalSharedContext : public SharedContext
 {
   private:
     const RootedObject scopeChain_; /* scope chain object for the script */
 
   public:
-    GlobalSharedContext(JSContext *cx, JSObject *scopeChain, bool strict)
-      : SharedContext(cx, strict),
+    GlobalSharedContext(ExclusiveContext *cx, JSObject *scopeChain,
+                        bool strict, bool extraWarnings)
+      : SharedContext(cx, strict, extraWarnings),
         scopeChain_(cx, scopeChain)
     {}
 
     ObjectBox *toObjectBox() { return NULL; }
     JSObject *scopeChain() const { return scopeChain_; }
 };
 
 inline GlobalSharedContext *
@@ -203,18 +206,18 @@ SharedContext::asGlobalSharedContext()
     return static_cast<GlobalSharedContext*>(this);
 }
 
 class ModuleBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings bindings;
 
-    ModuleBox(JSContext *cx, ObjectBox *traceListHead, Module *module,
-              ParseContext<FullParseHandler> *pc);
+    ModuleBox(ExclusiveContext *cx, ObjectBox *traceListHead, Module *module,
+              ParseContext<FullParseHandler> *pc, bool extraWarnings);
     ObjectBox *toObjectBox() { return this; }
     Module *module() const { return &object->as<Module>(); }
 };
 
 inline ModuleBox *
 SharedContext::asModuleBox()
 {
     JS_ASSERT(isModuleBox());
@@ -238,18 +241,19 @@ class FunctionBox : public ObjectBox, pu
 
     // Fields for use in heuristics.
     bool            usesArguments:1;  /* contains a free use of 'arguments' */
     bool            usesApply:1;      /* contains an f.apply() call */
 
     FunctionContextFlags funCxFlags;
 
     template <typename ParseHandler>
-    FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext<ParseHandler> *pc,
-                bool strict);
+    FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
+                ParseContext<ParseHandler> *pc,
+                bool strict, bool extraWarnings);
 
     ObjectBox *toObjectBox() { return this; }
     JSFunction *function() const { return &object->as<JSFunction>(); }
 
     bool isGenerator()              const { return funCxFlags.isGenerator; }
     bool mightAliasLocals()         const { return funCxFlags.mightAliasLocals; }
     bool hasExtensibleScope()       const { return funCxFlags.hasExtensibleScope; }
     bool needsDeclEnvObject()       const { return funCxFlags.needsDeclEnvObject; }
@@ -356,17 +360,17 @@ struct StmtInfoBase {
     bool isBlockScope:1;
 
     /* for (let ...) induced block scope */
     bool isForLetBlock:1;
 
     RootedAtom      label;          /* name of LABEL */
     Rooted<StaticBlockObject *> blockObj; /* block scope object */
 
-    StmtInfoBase(JSContext *cx)
+    StmtInfoBase(ExclusiveContext *cx)
         : isBlockScope(false), isForLetBlock(false), label(cx), blockObj(cx)
     {}
 
     bool maybeScope() const {
         return STMT_BLOCK <= type && type <= STMT_SUBROUTINE && type != STMT_WITH;
     }
 
     bool linksScope() const {
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -37,17 +37,18 @@ class SyntaxParseHandler
         NodeName,
         NodeGetProp,
         NodeString,
         NodeStringExprStatement,
         NodeLValue
     };
     typedef Definition::Kind DefinitionNode;
 
-    SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
+    SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
+                       TokenStream &tokenStream, bool foldConstants,
                        Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
       : lastAtom(NULL),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer *trc) {}
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -112,17 +112,17 @@ frontend::IsIdentifier(JSLinearString *s
 }
 
 bool
 frontend::IsKeyword(JSLinearString *str)
 {
     return FindKeyword(str->chars(), str->length()) != NULL;
 }
 
-TokenStream::SourceCoords::SourceCoords(JSContext *cx, uint32_t ln)
+TokenStream::SourceCoords::SourceCoords(ExclusiveContext *cx, uint32_t ln)
   : lineStartOffsets_(cx), initialLineNum_(ln), lastLineIndex_(0)
 {
     // This is actually necessary!  Removing it causes compile errors on
     // GCC and clang.  You could try declaring this:
     //
     //   const uint32_t TokenStream::SourceCoords::MAX_PTR;
     //
     // which fixes the GCC/clang error, but causes bustage on Windows.  Sigh.
@@ -257,55 +257,59 @@ TokenStream::SourceCoords::lineNumAndCol
 }
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4351)
 #endif
 
 /* Initialize members that aren't initialized in |init|. */
-TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
+TokenStream::TokenStream(ExclusiveContext *cx, const CompileOptions &options,
                          const jschar *base, size_t length, StrictModeGetter *smg,
                          AutoKeepAtoms& keepAtoms)
   : srcCoords(cx, options.lineno),
+    options_(options),
     tokens(),
     cursor(),
     lookahead(),
     lineno(options.lineno),
     flags(),
     linebase(base - options.column),
     prevLinebase(NULL),
     userbuf(cx, base - options.column, length + options.column), // See comment below
     filename(options.filename),
     sourceMap(NULL),
-    listenerTSData(),
     tokenbuf(cx),
-    version(options.version),
     cx(cx),
     originPrincipals(JSScript::normalizeOriginPrincipals(options.principals,
                                                          options.originPrincipals)),
     strictModeGetter(smg),
     tokenSkip(cx, &tokens),
     linebaseSkip(cx, &linebase),
     prevLinebaseSkip(cx, &prevLinebase)
 {
     // Column numbers are computed as offsets from the current line's base, so the
     // initial line's base must be included in the buffer. linebase and userbuf
     // were adjusted above, and if we are starting tokenization part way through
     // this line then adjust the next character.
     userbuf.setAddressOfNextRawChar(base);
 
-    if (originPrincipals)
-        JS_HoldPrincipals(originPrincipals);
+    JSContext *ncx = cx->asJSContext();
+    {
+        if (originPrincipals)
+            JS_HoldPrincipals(originPrincipals);
 
-    JSSourceHandler listener = cx->runtime()->debugHooks.sourceHandler;
-    void *listenerData = cx->runtime()->debugHooks.sourceHandlerData;
+        JSSourceHandler listener = ncx->runtime()->debugHooks.sourceHandler;
+        void *listenerData = ncx->runtime()->debugHooks.sourceHandlerData;
 
-    if (listener)
-        listener(options.filename, options.lineno, base, length, &listenerTSData, listenerData);
+        if (listener) {
+            void *listenerTSData;
+            listener(options.filename, options.lineno, base, length, &listenerTSData, listenerData);
+        }
+    }
 
     /*
      * This table holds all the token kinds that satisfy these properties:
      * - A single char long.
      * - Cannot be a prefix of any longer token (e.g. '+' is excluded because
      *   '+=' is a valid token).
      *
      * The few token kinds satisfying these properties cover roughly 35--45%
@@ -350,17 +354,17 @@ TokenStream::TokenStream(JSContext *cx, 
 #pragma warning(pop)
 #endif
 
 TokenStream::~TokenStream()
 {
     if (sourceMap)
         js_free(sourceMap);
     if (originPrincipals)
-        JS_DropPrincipals(cx->runtime(), originPrincipals);
+        JS_DropPrincipals(cx->asJSContext()->runtime(), originPrincipals);
 }
 
 /* Use the fastest available getc. */
 #if defined(HAVE_GETC_UNLOCKED)
 # define fast_getc getc_unlocked
 #elif defined(HAVE__GETC_NOLOCK)
 # define fast_getc _getc_nolock
 #else
@@ -578,17 +582,17 @@ TokenStream::seek(const Position &pos, c
 bool
 TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber,
                                            va_list args)
 {
     /* In strict mode code, this is an error, not merely a warning. */
     unsigned flags = JSREPORT_STRICT;
     if (strictMode)
         flags |= JSREPORT_ERROR;
-    else if (cx->hasExtraWarningsOption())
+    else if (options().extraWarningsOption)
         flags |= JSREPORT_WARNING;
     else
         return true;
 
     return reportCompileErrorNumberVA(offset, flags, errorNumber, args);
 }
 
 void
@@ -644,21 +648,26 @@ CompileError::~CompileError()
 }
 
 bool
 TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber,
                                         va_list args)
 {
     bool warning = JSREPORT_IS_WARNING(flags);
 
-    if (warning && cx->hasWErrorOption()) {
+    if (warning && options().werrorOption) {
         flags &= ~JSREPORT_WARNING;
         warning = false;
     }
 
+    if (!this->cx->isJSContext())
+        return warning;
+
+    JSContext *cx = this->cx->asJSContext();
+
     CompileError err(cx);
 
     err.report.flags = flags;
     err.report.errorNumber = errorNumber;
     err.report.filename = filename;
     err.report.originPrincipals = originPrincipals;
     err.report.lineno = srcCoords.lineNum(offset);
     err.report.column = srcCoords.columnIndex(offset);
@@ -757,17 +766,17 @@ TokenStream::reportWarning(unsigned erro
                                              errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 TokenStream::reportStrictWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args)
 {
-    if (!cx->hasExtraWarningsOption())
+    if (!options().extraWarningsOption)
         return true;
 
     return reportCompileErrorNumberVA(offset, JSREPORT_STRICT|JSREPORT_WARNING, errorNumber, args);
 }
 
 void
 TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...)
 {
@@ -898,17 +907,17 @@ TokenStream::newToken(ptrdiff_t adjust)
 
     // NOTE: tp->pos.end is not set until the very end of getTokenInternal().
     MOZ_MAKE_MEM_UNDEFINED(&tp->pos.end, sizeof(tp->pos.end));
 
     return tp;
 }
 
 JS_ALWAYS_INLINE JSAtom *
-TokenStream::atomize(JSContext *cx, CharBuffer &cb)
+TokenStream::atomize(ExclusiveContext *cx, CharBuffer &cb)
 {
     return AtomizeChars<CanGC>(cx, cb.begin(), cb.length());
 }
 
 #ifdef DEBUG
 bool
 IsTokenSane(Token *tp)
 {
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -354,22 +354,16 @@ struct CompileError {
       : cx(cx), message(NULL), argumentsType(ArgumentsAreUnicode)
     {
         mozilla::PodZero(&report);
     }
     ~CompileError();
     void throwError();
 };
 
-inline bool
-StrictModeFromContext(JSContext *cx)
-{
-    return cx->hasOption(JSOPTION_STRICT_MODE);
-}
-
 // Ideally, tokenizing would be entirely independent of context.  But the
 // strict mode flag, which is in SharedContext, affects tokenizing, and
 // TokenStream needs to see it.
 //
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream.
 //
 class StrictModeGetter {
@@ -428,39 +422,38 @@ class MOZ_STACK_CLASS TokenStream
     static const size_t ntokens = 4;                /* 1 current + 2 lookahead, rounded
                                                        to power of 2 to avoid divmod by 3 */
     static const unsigned maxLookahead = 2;
     static const unsigned ntokensMask = ntokens - 1;
 
   public:
     typedef Vector<jschar, 32> CharBuffer;
 
-    TokenStream(JSContext *cx, const CompileOptions &options,
+    TokenStream(ExclusiveContext *cx, const CompileOptions &options,
                 const jschar *base, size_t length, StrictModeGetter *smg,
                 AutoKeepAtoms& keepAtoms);
 
     ~TokenStream();
 
     /* Accessors. */
-    JSContext *getContext() const { return cx; }
     bool onCurrentLine(const TokenPos &pos) const { return srcCoords.isOnThisLine(pos.end, lineno); }
     const Token &currentToken() const { return tokens[cursor]; }
     bool isCurrentTokenType(TokenKind type) const {
         return currentToken().type == type;
     }
     bool isCurrentTokenType(TokenKind type1, TokenKind type2) const {
         TokenKind type = currentToken().type;
         return type == type1 || type == type2;
     }
     const CharBuffer &getTokenbuf() const { return tokenbuf; }
     const char *getFilename() const { return filename; }
     unsigned getLineno() const { return lineno; }
     unsigned getColumn() const { return userbuf.addressOfNextRawChar() - linebase - 1; }
-    JSVersion versionNumber() const { return VersionNumber(version); }
-    JSVersion versionWithFlags() const { return version; }
+    JSVersion versionNumber() const { return VersionNumber(options().version); }
+    JSVersion versionWithFlags() const { return options().version; }
     bool hadError() const { return !!(flags & TSF_HAD_ERROR); }
 
     bool isCurrentTokenEquality() const {
         return TokenKindIsEquality(currentToken().type);
     }
 
     bool isCurrentTokenRelational() const {
         return TokenKindIsRelational(currentToken().type);
@@ -500,17 +493,17 @@ class MOZ_STACK_CLASS TokenStream
 
   private:
     // These are private because they should only be called by the tokenizer
     // while tokenizing not by, for example, BytecodeEmitter.
     bool reportStrictModeError(unsigned errorNumber, ...);
     bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
 
     void onError();
-    static JSAtom *atomize(JSContext *cx, CharBuffer &cb);
+    static JSAtom *atomize(ExclusiveContext *cx, CharBuffer &cb);
     bool putIdentInTokenbuf(const jschar *identStart);
 
     /*
      * Enables flags in the associated tokenstream for the object lifetime.
      * Useful for lexically-scoped flag toggles.
      */
     class Flagger {
         TokenStream * const parent;
@@ -732,17 +725,17 @@ class MOZ_STACK_CLASS TokenStream
         uint32_t lineIndexOf(uint32_t offset) const;
 
         static const uint32_t MAX_PTR = UINT32_MAX;
 
         uint32_t lineIndexToNum(uint32_t lineIndex) const { return lineIndex + initialLineNum_; }
         uint32_t lineNumToIndex(uint32_t lineNum)   const { return lineNum   - initialLineNum_; }
 
       public:
-        SourceCoords(JSContext *cx, uint32_t ln);
+        SourceCoords(ExclusiveContext *cx, uint32_t ln);
 
         void add(uint32_t lineNum, uint32_t lineStartOffset);
         void fill(const SourceCoords &other);
 
         bool isOnThisLine(uint32_t offset, uint32_t lineNum) const {
             uint32_t lineIndex = lineNumToIndex(lineNum);
             JS_ASSERT(lineIndex + 1 < lineStartOffsets_.length());  // +1 due to sentinel
             return lineStartOffsets_[lineIndex] <= offset &&
@@ -751,27 +744,39 @@ class MOZ_STACK_CLASS TokenStream
 
         uint32_t lineNum(uint32_t offset) const;
         uint32_t columnIndex(uint32_t offset) const;
         void lineNumAndColumnIndex(uint32_t offset, uint32_t *lineNum, uint32_t *columnIndex) const;
     };
 
     SourceCoords srcCoords;
 
+    JSAtomState &names() const {
+        return cx->names();
+    }
+
+    ExclusiveContext *context() const {
+        return cx;
+    }
+
+    const CompileOptions &options() const {
+        return options_;
+    }
+
   private:
     /*
      * This is the low-level interface to the JS source code buffer.  It just
      * gets raw chars, basically.  TokenStreams functions are layered on top
      * and do some extra stuff like converting all EOL sequences to '\n',
      * tracking the line number, and setting the TSF_EOF flag.  (The "raw" in
      * "raw chars" refers to the lack of EOL sequence normalization.)
      */
     class TokenBuf {
       public:
-        TokenBuf(JSContext *cx, const jschar *buf, size_t length)
+        TokenBuf(ExclusiveContext *cx, const jschar *buf, size_t length)
           : base_(buf), limit_(buf + length), ptr(buf),
             skipBase(cx, &base_), skipLimit(cx, &limit_), skipPtr(cx, &ptr)
         { }
 
         bool hasRawChars() const {
             return ptr < limit_;
         }
 
@@ -886,33 +891,34 @@ class MOZ_STACK_CLASS TokenStream
     void skipChars(int n) {
         while (--n >= 0)
             getChar();
     }
 
     void updateLineInfoForEOL();
     void updateFlagsForEOL();
 
+    // Options used for parsing/tokenizing.
+    const CompileOptions &options_;
+
     Token               tokens[ntokens];/* circular token buffer */
     unsigned            cursor;         /* index of last parsed token */
     unsigned            lookahead;      /* count of lookahead tokens */
     unsigned            lineno;         /* current line number */
     unsigned            flags;          /* flags -- see above */
     const jschar        *linebase;      /* start of current line;  points into userbuf */
     const jschar        *prevLinebase;  /* start of previous line;  NULL if on the first line */
     TokenBuf            userbuf;        /* user input buffer */
     const char          *filename;      /* input filename or null */
     jschar              *sourceMap;     /* source map's filename or null */
-    void                *listenerTSData;/* listener data for this TokenStream */
     CharBuffer          tokenbuf;       /* current token string buffer */
     int8_t              oneCharTokens[128];  /* table of one-char tokens */
     bool                maybeEOL[256];       /* probabilistic EOL lookup table */
     bool                maybeStrSpecial[256];/* speeds up string scanning */
-    JSVersion           version;        /* (i.e. to identify keywords) */
-    JSContext           *const cx;
+    ExclusiveContext    *const cx;
     JSPrincipals        *const originPrincipals;
     StrictModeGetter    *strictModeGetter; /* used to test for strict mode */
 
     /*
      * The tokens array stores pointers to JSAtoms. These are rooted by the
      * atoms table using AutoKeepAtoms in the Parser. This SkipRoot tells the
      * exact rooting analysis to ignore the atoms in the tokens array.
      */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -567,26 +567,30 @@ AutoGCRooter::trace(JSTracer *trc)
     JS_ASSERT(tag_ >= 0);
     if (Value *vp = static_cast<AutoArrayRooter *>(this)->array)
         MarkValueRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer *trc)
 {
-    for (js::AutoGCRooter *gcr = trc->runtime->autoGCRooters; gcr; gcr = gcr->down)
-        gcr->trace(trc);
+    for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
+        for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down)
+            gcr->trace(trc);
+    }
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer *trc)
 {
-    for (js::AutoGCRooter *gcr = trc->runtime->autoGCRooters; gcr; gcr = gcr->down) {
-        if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
-            gcr->trace(trc);
+    for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
+        for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) {
+            if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
+                gcr->trace(trc);
+        }
     }
 }
 
 void
 AutoHashableValueRooter::trace(JSTracer *trc)
 {
     MarkValueRoot(trc, reinterpret_cast<Value*>(&value), "AutoHashableValueRooter");
 }
--- a/js/src/gdb/tests/test-JSString.cpp
+++ b/js/src/gdb/tests/test-JSString.cpp
@@ -1,10 +1,11 @@
 #include "gdb-tests.h"
 #include "jsatom.h"
+#include "jscntxt.h"
 
 // When JSGC_ANALYSIS is #defined, Rooted<JSFlatString*> needs the definition
 // of JSFlatString in order to figure out its ThingRootKind
 #include "vm/String.h"
 
 FRAGMENT(JSString, simple) {
   JS::Rooted<JSString *> empty(cx, JS_NewStringCopyN(cx, NULL, 0));
   JS::Rooted<JSString *> x(cx, JS_NewStringCopyN(cx, "x", 1));
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -1447,17 +1447,17 @@ class MOZ_STACK_CLASS ModuleCompiler
         msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
         if (!slowFunctions_.empty()) {
             slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
             if (!slowFuns)
                 return;
             for (unsigned i = 0; i < slowFunctions_.length(); i++) {
                 SlowFunction &func = slowFunctions_[i];
                 JSAutoByteString name;
-                if (!js_AtomToPrintableString(cx_, func.name, &name))
+                if (!AtomToPrintableString(cx_, func.name, &name))
                     return;
                 slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
                                            name.ptr(), func.line, func.column, func.ms,
                                            i+1 < slowFunctions_.length() ? ", " : ""));
                 if (!slowFuns)
                     return;
             }
         }
--- a/js/src/ion/AsmJSLink.cpp
+++ b/js/src/ion/AsmJSLink.cpp
@@ -441,17 +441,17 @@ SendFunctionsToVTune(JSContext *cx, AsmJ
         uint8_t *end   = base + func.endCodeOffset;
         JS_ASSERT(end >= start);
 
         unsigned method_id = iJIT_GetNewMethodID();
         if (method_id == 0)
             return false;
 
         JSAutoByteString bytes;
-        const char *method_name = js_AtomToPrintableString(cx, func.name, &bytes);
+        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
         if (!method_name)
             return false;
 
         iJIT_Method_Load method;
         method.method_id = method_id;
         method.method_name = const_cast<char *>(method_name);
         method.method_load_address = (void *)start;
         method.method_size = unsigned(end - start);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -4442,17 +4442,17 @@ IonBuilder::createThisScriptedSingleton(
     if (!proto)
         return NULL;
 
     if (!target->nonLazyScript()->types)
         return NULL;
 
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
-    types::TypeObject *type = proto->getNewType(cx, &ObjectClass, target);
+    types::TypeObject *type = cx->getNewType(&ObjectClass, proto.get(), target);
     if (!type)
         return NULL;
     if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(type)))
         return NULL;
 
     RootedObject templateObject(cx, CreateThisForFunctionWithProto(cx, target, proto, TenuredObject));
     if (!templateObject)
         return NULL;
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -797,16 +797,22 @@ MacroAssembler::performOsr()
     addPtr(osrEntry, code);
 
     // To simplify stack handling, we create an intermediate OSR frame, that
     // looks like a JS frame with no argv.
     enterOsr(calleeToken, code);
     ret();
 }
 
+static void
+ReportOverRecursed(JSContext *cx)
+{
+    js_ReportOverRecursed(cx);
+}
+
 void
 MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
 {
     enterExitFrame();
 
     Label exception;
     Label baseline;
 
@@ -818,17 +824,17 @@ MacroAssembler::generateBailoutTail(Regi
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OK), &baseline);
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), &exception);
 
     // Fall-through: overrecursed.
     {
         loadJSContext(ReturnReg);
         setupUnalignedABICall(1, scratch);
         passABIArg(ReturnReg);
-        callWithABI(JS_FUNC_TO_DATA_PTR(void *, js_ReportOverRecursed));
+        callWithABI(JS_FUNC_TO_DATA_PTR(void *, ReportOverRecursed));
         jump(&exception);
     }
 
     bind(&exception);
     {
         handleException();
     }
 
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -251,18 +251,18 @@ do {                                    
         if (ret != TP_SUCCESS)                                                  \
             return ret;                                                         \
         *res = (vsZero OP 0) == EXPECTED;                                       \
     }                                                                           \
     return TP_SUCCESS;                                                          \
 } while(0)
 
 static ParallelResult
-ParCompareStrings(ForkJoinSlice *slice, HandleString str1,
-                  HandleString str2, int32_t *res)
+ParCompareStrings(ForkJoinSlice *slice, JSString *str1,
+                  JSString *str2, int32_t *res)
 {
     if (!str1->isLinear())
         return TP_RETRY_SEQUENTIALLY;
     if (!str2->isLinear())
         return TP_RETRY_SEQUENTIALLY;
     JSLinearString &linearStr1 = str1->asLinear();
     JSLinearString &linearStr2 = str2->asLinear();
     if (!CompareChars(linearStr1.chars(), linearStr1.length(),
@@ -278,19 +278,17 @@ ParCompareMaybeStrings(ForkJoinSlice *sl
                        HandleValue v1,
                        HandleValue v2,
                        int32_t *res)
 {
     if (!v1.isString())
         return TP_RETRY_SEQUENTIALLY;
     if (!v2.isString())
         return TP_RETRY_SEQUENTIALLY;
-    RootedString str1(slice->perThreadData, v1.toString());
-    RootedString str2(slice->perThreadData, v2.toString());
-    return ParCompareStrings(slice, str1, str2, res);
+    return ParCompareStrings(slice, v1.toString(), v2.toString(), res);
 }
 
 template<bool Equal>
 ParallelResult
 ParLooselyEqualImpl(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
 {
     PAR_RELATIONAL_OP(==, Equal);
 }
--- a/js/src/jsalloc.cpp
+++ b/js/src/jsalloc.cpp
@@ -8,16 +8,16 @@
 
 #include "jscntxt.h"
 
 using namespace js;
 
 void *
 TempAllocPolicy::onOutOfMemory(void *p, size_t nbytes)
 {
-    return cx_->runtime()->onOutOfMemory(p, nbytes, cx_);
+    return static_cast<ThreadSafeContext *>(cx_)->onOutOfMemory(p, nbytes);
 }
 
 void
 TempAllocPolicy::reportAllocOverflow() const
 {
-    js_ReportAllocationOverflow(cx_);
+    js_ReportAllocationOverflow(static_cast<ThreadSafeContext *>(cx_));
 }
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -8,16 +8,18 @@
 #define jsalloc_h
 
 #include "js/Utility.h"
 
 struct JSContext;
 
 namespace js {
 
+class ContextFriendFields;
+
 /*
  * Allocation policies.  These model the concept:
  *  - public copy constructor, assignment, destructor
  *  - void *malloc_(size_t)
  *      Responsible for OOM reporting on NULL return value.
  *  - void *calloc_(size_t)
  *      Responsible for OOM reporting on NULL return value.
  *  - void *realloc_(size_t)
@@ -47,30 +49,27 @@ class SystemAllocPolicy
  * the lifetime of the container, this policy may only be used for containers
  * whose lifetime is a shorter than the given JSContext.
  *
  * FIXME bug 647103 - rewrite this in terms of temporary allocation functions,
  * not the system ones.
  */
 class TempAllocPolicy
 {
-    JSContext *const cx_;
+    ContextFriendFields *const cx_;
 
     /*
      * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
      * code bloat.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes);
 
   public:
-    TempAllocPolicy(JSContext *cx) : cx_(cx) {}
-
-    JSContext *context() const {
-        return cx_;
-    }
+    TempAllocPolicy(JSContext *cx) : cx_((ContextFriendFields *) cx) {} // :(
+    TempAllocPolicy(ContextFriendFields *cx) : cx_(cx) {}
 
     void *malloc_(size_t bytes) {
         void *p = js_malloc(bytes);
         if (JS_UNLIKELY(!p))
             p = onOutOfMemory(NULL, bytes);
         return p;
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -811,17 +811,16 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     gcValidate(true),
     gcFullCompartmentChecks(false),
     gcCallback(NULL),
     gcSliceCallback(NULL),
     gcFinalizeCallback(NULL),
     analysisPurgeCallback(NULL),
     analysisPurgeTriggerBytes(0),
     gcMallocBytes(0),
-    autoGCRooters(NULL),
     scriptAndCountsVector(NULL),
     NaNValue(UndefinedValue()),
     negativeInfinityValue(UndefinedValue()),
     positiveInfinityValue(UndefinedValue()),
     emptyString(NULL),
     sourceHook(NULL),
     debugMode(false),
     spsProfiler(thisFromCtor()),
@@ -5176,16 +5175,20 @@ JS::CompileOptions::CompileOptions(JSCon
       lineno(1),
       column(0),
       element(NullPtr()),
       compileAndGo(cx->hasOption(JSOPTION_COMPILE_N_GO)),
       forEval(false),
       noScriptRval(cx->hasOption(JSOPTION_NO_SCRIPT_RVAL)),
       selfHostingMode(false),
       canLazilyParse(true),
+      strictOption(cx->hasOption(JSOPTION_STRICT_MODE)),
+      extraWarningsOption(cx->hasExtraWarningsOption()),
+      werrorOption(cx->hasWErrorOption()),
+      asmJSOption(cx->hasOption(JSOPTION_ASMJS)),
       sourcePolicy(SAVE_SOURCE)
 {
 }
 
 JSScript *
 JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options,
             const jschar *chars, size_t length)
 {
@@ -5306,17 +5309,18 @@ JS_BufferIsCompilableUnit(JSContext *cx,
      * Return true on any out-of-memory error, so our caller doesn't try to
      * collect more buffered source.
      */
     result = JS_TRUE;
     exnState = JS_SaveExceptionState(cx);
     {
         CompileOptions options(cx);
         options.setCompileAndGo(false);
-        Parser<frontend::FullParseHandler> parser(cx, options, chars, length,
+        Parser<frontend::FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
+                                                  options, chars, length,
                                                   /* foldConstants = */ true, NULL, NULL);
         older = JS_SetErrorReporter(cx, NULL);
         if (!parser.parse(obj) && parser.tokenStream.isUnexpectedEOF()) {
             /*
              * We ran into an error. If it was because we ran out of
              * source, we return false so our caller knows to try to
              * collect more buffered source.
              */
@@ -7117,17 +7121,28 @@ JS_CallOnce(JSCallOnceType *once, JSInit
         return func();
     } else {
         return JS_TRUE;
     }
 #endif
 }
 
 AutoGCRooter::AutoGCRooter(JSContext *cx, ptrdiff_t tag)
-  : down(cx->runtime()->autoGCRooters), tag_(tag), stackTop(&cx->runtime()->autoGCRooters)
+  : down(ContextFriendFields::get(cx)->autoGCRooters),
+    tag_(tag),
+    stackTop(&ContextFriendFields::get(cx)->autoGCRooters)
+{
+    JS_ASSERT(this != *stackTop);
+    *stackTop = this;
+}
+
+AutoGCRooter::AutoGCRooter(ContextFriendFields *cx, ptrdiff_t tag)
+  : down(cx->autoGCRooters),
+    tag_(tag),
+    stackTop(&cx->autoGCRooters)
 {
     JS_ASSERT(this != *stackTop);
     *stackTop = this;
 }
 
 #ifdef DEBUG
 JS_PUBLIC_API(void)
 JS::AssertArgumentsAreSane(JSContext *cx, const JS::Value &value)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -49,16 +49,17 @@ class StableCharPtr : public CharPtr {
 
 #if defined JS_THREADSAFE && defined DEBUG
 
 class JS_PUBLIC_API(AutoCheckRequestDepth)
 {
     JSContext *cx;
   public:
     AutoCheckRequestDepth(JSContext *cx);
+    AutoCheckRequestDepth(js::ContextFriendFields *cx);
     ~AutoCheckRequestDepth();
 };
 
 # define CHECK_REQUEST(cx) \
     JS::AutoCheckRequestDepth _autoCheckRequestDepth(cx)
 
 #else
 
@@ -79,16 +80,17 @@ AssertArgumentsAreSane(JSContext *cx, co
 inline void AssertArgumentsAreSane(JSContext *cx, const Value &v) {
     /* Do nothing */
 }
 #endif /* DEBUG */
 
 class JS_PUBLIC_API(AutoGCRooter) {
   public:
     AutoGCRooter(JSContext *cx, ptrdiff_t tag);
+    AutoGCRooter(js::ContextFriendFields *cx, ptrdiff_t tag);
 
     ~AutoGCRooter() {
         JS_ASSERT(this == *stackTop);
         *stackTop = down;
     }
 
     /* Implemented in gc/RootMarking.cpp. */
     inline void trace(JSTracer *trc);
@@ -222,16 +224,23 @@ class AutoVectorRooter : protected AutoG
   public:
     explicit AutoVectorRooter(JSContext *cx, ptrdiff_t tag
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, tag), vector(cx), vectorRoot(cx, &vector)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
+    explicit AutoVectorRooter(js::ContextFriendFields *cx, ptrdiff_t tag
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : AutoGCRooter(cx, tag), vector(cx), vectorRoot(cx, &vector)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
     typedef T ElementType;
 
     size_t length() const { return vector.length(); }
     bool empty() const { return vector.empty(); }
 
     bool append(const T &v) { return vector.append(v); }
     bool append(const AutoVectorRooter<T> &other) {
         return vector.append(other.vector);
@@ -573,17 +582,24 @@ class AutoObjectVector : public AutoVect
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoFunctionVector : public AutoVectorRooter<JSFunction *>
 {
   public:
     explicit AutoFunctionVector(JSContext *cx
-                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : AutoVectorRooter<JSFunction *>(cx, FUNVECTOR)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    explicit AutoFunctionVector(js::ContextFriendFields *cx
+                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : AutoVectorRooter<JSFunction *>(cx, FUNVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
@@ -601,17 +617,18 @@ class AutoScriptVector : public AutoVect
 };
 
 /*
  * Cutsom rooting behavior for internal and external clients.
  */
 class JS_PUBLIC_API(CustomAutoRooter) : private AutoGCRooter
 {
   public:
-    explicit CustomAutoRooter(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    template <typename CX>
+    explicit CustomAutoRooter(CX *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, CUSTOM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   protected:
@@ -3933,16 +3950,20 @@ struct JS_PUBLIC_API(CompileOptions) {
     unsigned lineno;
     unsigned column;
     HandleObject element;
     bool compileAndGo;
     bool forEval;
     bool noScriptRval;
     bool selfHostingMode;
     bool canLazilyParse;
+    bool strictOption;
+    bool extraWarningsOption;
+    bool werrorOption;
+    bool asmJSOption;
     enum SourcePolicy {
         NO_SOURCE,
         LAZY_SOURCE,
         SAVE_SOURCE
     } sourcePolicy;
 
     explicit CompileOptions(JSContext *cx, JSVersion version = JSVERSION_UNKNOWN);
     CompileOptions &setPrincipals(JSPrincipals *p) { principals = p; return *this; }
@@ -4464,16 +4485,18 @@ class JSAutoByteString
 
     char *encodeLatin1(JSContext *cx, JSString *str) {
         JS_ASSERT(!mBytes);
         JS_ASSERT(cx);
         mBytes = JS_EncodeString(cx, str);
         return mBytes;
     }
 
+    char *encodeLatin1(js::ContextFriendFields *cx, JSString *str);
+
     char *encodeUtf8(JSContext *cx, JSString *str) {
         JS_ASSERT(!mBytes);
         JS_ASSERT(cx);
         mBytes = JS_EncodeStringToUTF8(cx, str);
         return mBytes;
     }
 
     void clear() {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -418,17 +418,20 @@ js::CanonicalizeArrayLengthValue(JSConte
         return false;
 
     double d;
     if (!ToNumber(cx, v, &d))
         return false;
     if (d == *newLen)
         return true;
 
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
+    if (!cx->isJSContext())
+        return false;
+
+    JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
     return false;
 }
 
 /* ES6 20130308 draft 8.4.2.4 ArraySetLength */
 bool
 js::ArraySetLength(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, unsigned attrs,
                    HandleValue value, bool setterIsStrict)
 {
@@ -456,24 +459,24 @@ js::ArraySetLength(JSContext *cx, Handle
     uint32_t oldLen = arr->length();
 
     /* Steps 8-9 for arrays with non-writable length. */
     if (!lengthIsWritable) {
         if (newLen == oldLen)
             return true;
 
         if (setterIsStrict) {
-            return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
+            return JS_ReportErrorFlagsAndNumber(cx->asJSContext(),
+                                                JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                                 JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
         }
 
-        return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
+        return JSObject::reportReadOnly(cx->asJSContext(), id, JSREPORT_STRICT | JSREPORT_WARNING);
     }
 
-
     /* Step 8. */
     bool succeeded = true;
     do {
         // The initialized length and capacity of an array only need updating
         // when non-hole elements are added or removed, which doesn't happen
         // when array length stays the same or increases.
         if (newLen >= oldLen)
             break;
@@ -652,17 +655,18 @@ js::ArraySetLength(JSContext *cx, Handle
             return false;
         return arr->reportNotConfigurable(cx, elementId);
     }
 
     return true;
 }
 
 bool
-js::WouldDefinePastNonwritableLength(JSContext *cx, HandleObject obj, uint32_t index, bool strict,
+js::WouldDefinePastNonwritableLength(ExclusiveContext *cx,
+                                     HandleObject obj, uint32_t index, bool strict,
                                      bool *definesPast)
 {
     if (!obj->is<ArrayObject>()) {
         *definesPast = false;
         return true;
     }
 
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
@@ -673,23 +677,29 @@ js::WouldDefinePastNonwritableLength(JSC
     }
 
     if (arr->lengthIsWritable()) {
         *definesPast = false;
         return true;
     }
 
     *definesPast = true;
-    if (!strict && !cx->hasExtraWarningsOption())
+
+    if (!cx->isJSContext())
+        return true;
+
+    JSContext *ncx = cx->asJSContext();
+
+    if (!strict && !ncx->hasExtraWarningsOption())
         return true;
 
     // Error in strict mode code or warn with strict option.
     // XXX include the index and maybe array length in the error message
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
-    return JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
+    return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, NULL,
                                         JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
 }
 
 static JSBool
 array_addProperty(JSContext *cx, HandleObject obj, HandleId id,
                   MutableHandleValue vp)
 {
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
@@ -763,17 +773,17 @@ Class ArrayObject::class_ = {
         NULL,       /* outerObject */
         NULL,       /* innerObject */
         NULL,       /* iteratorObject  */
         false,      /* isWrappedNative */
     }
 };
 
 static bool
-AddLengthProperty(JSContext *cx, HandleObject obj)
+AddLengthProperty(ExclusiveContext *cx, HandleObject obj)
 {
     /*
      * Add the 'length' property for a newly created array,
      * and update the elements to be an empty array owned by the object.
      * The shared emptyObjectElements singleton cannot be used for slow arrays,
      * as accesses to 'length' will use the elements header.
      */
 
@@ -2878,17 +2888,17 @@ js_InitArrayClass(JSContext *cx, HandleO
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
     if (!proto)
         return NULL;
 
-    RootedTypeObject type(cx, proto->getNewType(cx, &ArrayObject::class_));
+    RootedTypeObject type(cx, cx->getNewType(&ArrayObject::class_, proto.get()));
     if (!type)
         return NULL;
 
     JSObject *metadata = NULL;
     if (!NewObjectMetadata(cx, &metadata))
         return NULL;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
@@ -2936,17 +2946,17 @@ js_InitArrayClass(JSContext *cx, HandleO
     return arrayProto;
 }
 
 /*
  * Array allocation functions.
  */
 
 static inline bool
-EnsureNewArrayElements(JSContext *cx, JSObject *obj, uint32_t length)
+EnsureNewArrayElements(ExclusiveContext *cx, JSObject *obj, uint32_t length)
 {
     /*
      * If ensureElements creates dynamically allocated slots, then having
      * fixedSlots is a waste.
      */
     DebugOnly<uint32_t> cap = obj->getDenseCapacity();
 
     if (!obj->ensureElements(cx, length))
@@ -2954,110 +2964,113 @@ EnsureNewArrayElements(JSContext *cx, JS
 
     JS_ASSERT_IF(cap, !obj->hasDynamicElements());
 
     return true;
 }
 
 template<bool allocateCapacity>
 static JS_ALWAYS_INLINE ArrayObject *
-NewArray(JSContext *cx, uint32_t length, JSObject *protoArg, NewObjectKind newKind = GenericObject)
+NewArray(ExclusiveContext *cxArg, uint32_t length,
+         JSObject *protoArg, NewObjectKind newKind = GenericObject)
 {
     gc::AllocKind allocKind = GuessArrayGCKind(length);
     JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
     allocKind = GetBackgroundAllocKind(allocKind);
 
-    NewObjectCache &cache = cx->runtime()->newObjectCache;
-
     NewObjectCache::EntryIndex entry = -1;
-    if (newKind == GenericObject &&
-        !cx->compartment()->objectMetadataCallback &&
-        cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
-    {
-        RootedObject obj(cx, cache.newObjectFromHit(cx, entry,
-                                                    GetInitialHeap(newKind, &ArrayObject::class_)));
-        if (obj) {
-            /* Fixup the elements pointer and length, which may be incorrect. */
-            Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-            arr->setFixedElements();
-            ArrayObject::setLength(cx, arr, length);
-            if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
-                return NULL;
-            return arr;
+    if (JSContext *cx = cxArg->maybeJSContext()) {
+        NewObjectCache &cache = cx->runtime()->newObjectCache;
+        if (newKind == GenericObject &&
+            !cx->compartment()->objectMetadataCallback &&
+            cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
+        {
+            RootedObject obj(cx, cache.newObjectFromHit(cx, entry,
+                                                        GetInitialHeap(newKind, &ArrayObject::class_)));
+            if (obj) {
+                /* Fixup the elements pointer and length, which may be incorrect. */
+                Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
+                arr->setFixedElements();
+                ArrayObject::setLength(cx, arr, length);
+                if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
+                    return NULL;
+                return arr;
+            }
         }
     }
 
-    RootedObject proto(cx, protoArg);
+    RootedObject proto(cxArg, protoArg);
     if (protoArg)
         JS::PoisonPtr(&protoArg);
 
-    if (!proto && !FindProto(cx, &ArrayObject::class_, &proto))
+    if (!proto && !FindProto(cxArg, &ArrayObject::class_, &proto))
         return NULL;
 
-    RootedTypeObject type(cx, proto->getNewType(cx, &ArrayObject::class_));
+    RootedTypeObject type(cxArg, cxArg->getNewType(&ArrayObject::class_, proto.get()));
     if (!type)
         return NULL;
 
     JSObject *metadata = NULL;
-    if (!NewObjectMetadata(cx, &metadata))
+    if (!NewObjectMetadata(cxArg, &metadata))
         return NULL;
 
     /*
      * Get a shape with zero fixed slots, regardless of the size class.
      * See JSObject::createArray.
      */
-    RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
-                                                      cx->global(), metadata, gc::FINALIZE_OBJECT0));
+    RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
+                                                         TaggedProto(proto), cxArg->global(),
+                                                         metadata, gc::FINALIZE_OBJECT0));
     if (!shape)
         return NULL;
 
-    RootedObject obj(cx, JSObject::createArray(cx, allocKind,
-                                               GetInitialHeap(newKind, &ArrayObject::class_),
-                                               shape, type, length));
-    if (!obj)
+    Rooted<ArrayObject*> arr(cxArg, JSObject::createArray(cxArg, allocKind,
+                                                          GetInitialHeap(newKind, &ArrayObject::class_),
+                                                          shape, type, length));
+    if (!arr)
         return NULL;
 
-    Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-
     if (shape->isEmptyShape()) {
-        if (!AddLengthProperty(cx, arr))
+        if (!AddLengthProperty(cxArg, arr))
             return NULL;
         shape = arr->lastProperty();
-        EmptyShape::insertInitialShape(cx, shape, proto);
+        EmptyShape::insertInitialShape(cxArg, shape, proto);
     }
 
-    if (newKind == SingletonObject && !JSObject::setSingletonType(cx, arr))
+    if (newKind == SingletonObject && !JSObject::setSingletonType(cxArg, arr))
         return NULL;
 
-    if (entry != -1)
-        cache.fillGlobal(entry, &ArrayObject::class_, cx->global(), allocKind, arr);
-
-    if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
+    if (entry != -1) {
+        cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_,
+                                                                   cxArg->global(), allocKind, arr);
+    }
+
+    if (allocateCapacity && !EnsureNewArrayElements(cxArg, arr, length))
         return NULL;
 
-    Probes::createObject(cx, arr);
+    Probes::createObject(cxArg, arr);
     return arr;
 }
 
 ArrayObject * JS_FASTCALL
 js::NewDenseEmptyArray(JSContext *cx, JSObject *proto /* = NULL */,
                        NewObjectKind newKind /* = GenericObject */)
 {
     return NewArray<false>(cx, 0, proto, newKind);
 }
 
 ArrayObject * JS_FASTCALL
-js::NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto /* = NULL */,
+js::NewDenseAllocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = NULL */,
                            NewObjectKind newKind /* = GenericObject */)
 {
     return NewArray<true>(cx, length, proto, newKind);
 }
 
 ArrayObject * JS_FASTCALL
-js::NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto /* = NULL */,
+js::NewDenseUnallocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = NULL */,
                              NewObjectKind newKind /* = GenericObject */)
 {
     return NewArray<false>(cx, length, proto, newKind);
 }
 
 ArrayObject *
 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset,
                         JSObject *proto /* = NULL */)
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -45,25 +45,25 @@ class ArrayObject;
 
 /* Create a dense array with no capacity allocated, length set to 0. */
 extern ArrayObject * JS_FASTCALL
 NewDenseEmptyArray(JSContext *cx, JSObject *proto = NULL,
                    NewObjectKind newKind = GenericObject);
 
 /* Create a dense array with length and capacity == 'length', initialized length set to 0. */
 extern ArrayObject * JS_FASTCALL
-NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto = NULL,
+NewDenseAllocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto = NULL,
                        NewObjectKind newKind = GenericObject);
 
 /*
  * Create a dense array with a set length, but without allocating space for the
  * contents. This is useful, e.g., when accepting length from the user.
  */
 extern ArrayObject * JS_FASTCALL
-NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto = NULL,
+NewDenseUnallocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto = NULL,
                          NewObjectKind newKind = GenericObject);
 
 /* Create a dense array with a copy of the dense array elements in src. */
 extern ArrayObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset, JSObject *proto = NULL);
 
 /* Create a dense array from the given array values, which must be rooted */
 extern ArrayObject *
@@ -71,17 +71,18 @@ NewDenseCopiedArray(JSContext *cx, uint3
                     NewObjectKind newKind = GenericObject);
 
 /*
  * Determines whether a write to the given element on |obj| should fail because
  * |obj| is an Array with a non-writable length, and writing that element would
  * increase the length of the array.
  */
 extern bool
-WouldDefinePastNonwritableLength(JSContext *cx, HandleObject obj, uint32_t index, bool strict,
+WouldDefinePastNonwritableLength(ExclusiveContext *cx,
+                                 HandleObject obj, uint32_t index, bool strict,
                                  bool *definesPast);
 
 /*
  * Canonicalize |vp| to a uint32_t value potentially suitable for use as an
  * array length.
  */
 extern bool
 CanonicalizeArrayLengthValue(JSContext *cx, HandleValue v, uint32_t *canonicalized);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -32,19 +32,19 @@
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 using mozilla::ArrayLength;
 using mozilla::RangedPtr;
 
 const char *
-js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes)
+js::AtomToPrintableString(ExclusiveContext *cx, JSAtom *atom, JSAutoByteString *bytes)
 {
-    return js_ValueToPrintable(cx, StringValue(atom), bytes);
+    return js_ValueToPrintable(cx->asJSContext(), StringValue(atom), bytes);
 }
 
 const char * const js::TypeStrings[] = {
     js_undefined_str,
     js_object_str,
     js_function_str,
     js_string_str,
     js_number_str,
@@ -227,32 +227,32 @@ enum OwnCharsBehavior
 
 /*
  * When the jschars reside in a freshly allocated buffer the memory can be used
  * as a new JSAtom's storage without copying. The contract is that the caller no
  * longer owns the memory and this method is responsible for freeing the memory.
  */
 JS_ALWAYS_INLINE
 static JSAtom *
-AtomizeAndTakeOwnership(JSContext *cx, jschar *tbchars, size_t length, InternBehavior ib)
+AtomizeAndTakeOwnership(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehavior ib)
 {
     JS_ASSERT(tbchars[length] == 0);
 
-    if (JSAtom *s = cx->runtime()->staticStrings.lookup(tbchars, length)) {
+    if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) {
         js_free(tbchars);
         return s;
     }
 
     /*
      * If a GC occurs at js_NewStringCopy then |p| will still have the correct
      * hash, allowing us to avoid rehashing it. Even though the hash is
      * unchanged, we need to re-lookup the table position because a last-ditch
      * GC will potentially free some table entries.
      */
-    AtomSet& atoms = cx->runtime()->atoms;
+    AtomSet& atoms = cx->atoms();
     AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length));
     SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         js_free(tbchars);
         return atom;
     }
@@ -264,40 +264,40 @@ AtomizeAndTakeOwnership(JSContext *cx, j
         js_free(tbchars);
         return NULL;
     }
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
     if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length),
                              AtomStateEntry(atom, bool(ib)))) {
-        JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
+        js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return NULL;
     }
 
     return atom;
 }
 
 /* |tbchars| must not point into an inline or short string. */
 template <AllowGC allowGC>
 JS_ALWAYS_INLINE
 static JSAtom *
-AtomizeAndCopyChars(JSContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
+AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
 {
-    if (JSAtom *s = cx->runtime()->staticStrings.lookup(tbchars, length))
+    if (JSAtom *s = cx->staticStrings().lookup(tbchars, length))
          return s;
 
     /*
      * If a GC occurs at js_NewStringCopy then |p| will still have the correct
      * hash, allowing us to avoid rehashing it. Even though the hash is
      * unchanged, we need to re-lookup the table position because a last-ditch
      * GC will potentially free some table entries.
      */
 
-    AtomSet& atoms = cx->runtime()->atoms;
+    AtomSet& atoms = cx->atoms();
     AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(tbchars, length));
     SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         return atom;
     }
 
@@ -306,67 +306,68 @@ AtomizeAndCopyChars(JSContext *cx, const
     JSFlatString *flat = js_NewStringCopyN<allowGC>(cx, tbchars, length);
     if (!flat)
         return NULL;
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
     if (!atoms.relookupOrAdd(p, AtomHasher::Lookup(tbchars, length),
                              AtomStateEntry(atom, bool(ib)))) {
-        JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
+        js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return NULL;
     }
 
     return atom;
 }
 
 template <AllowGC allowGC>
 JSAtom *
-js::AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib /* = js::DoNotInternAtom */)
+js::AtomizeString(ExclusiveContext *cx, JSString *str,
+                  js::InternBehavior ib /* = js::DoNotInternAtom */)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
         if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
             return &atom;
 
-        AtomSet::Ptr p = cx->runtime()->atoms.lookup(AtomHasher::Lookup(&atom));
+        AtomSet::Ptr p = cx->atoms().lookup(AtomHasher::Lookup(&atom));
         JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
         JS_ASSERT(p->asPtr() == &atom);
         JS_ASSERT(ib == InternAtom);
         p->setTagged(bool(ib));
         return &atom;
     }
 
-    const jschar *chars = str->getChars(cx);
+    const jschar *chars = str->getChars(cx->asJSContext());
     if (!chars)
         return NULL;
 
     if (JSAtom *atom = AtomizeAndCopyChars<NoGC>(cx, chars, str->length(), ib))
         return atom;
 
     if (!allowGC)
         return NULL;
 
-    JSLinearString *linear = str->ensureLinear(cx);
+    JSLinearString *linear = str->ensureLinear(cx->asJSContext());
     if (!linear)
         return NULL;
 
     JS_ASSERT(linear->length() <= JSString::MAX_LENGTH);
     return AtomizeAndCopyChars<CanGC>(cx, linear->chars(), linear->length(), ib);
 }
 
 template JSAtom *
-js::AtomizeString<CanGC>(JSContext *cx, JSString *str, js::InternBehavior ib);
+js::AtomizeString<CanGC>(ExclusiveContext *cx, JSString *str, InternBehavior ib);
 
 template JSAtom *
-js::AtomizeString<NoGC>(JSContext *cx, JSString *str, js::InternBehavior ib);
+js::AtomizeString<NoGC>(ExclusiveContext *cx, JSString *str, InternBehavior ib);
 
 JSAtom *
-js::Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib)
+js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     static const unsigned ATOMIZE_BUF_MAX = 32;
     if (length < ATOMIZE_BUF_MAX) {
@@ -374,47 +375,53 @@ js::Atomize(JSContext *cx, const char *b
          * Avoiding the malloc in InflateString on shorter strings saves us
          * over 20,000 malloc calls on mozilla browser startup. This compares to
          * only 131 calls where the string is longer than a 31 char (net) buffer.
          * The vast majority of atomized strings are already in the hashtable. So
          * js::AtomizeString rarely has to copy the temp string we make.
          */
         jschar inflated[ATOMIZE_BUF_MAX];
         size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
-        InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
+        if (!InflateStringToBuffer(cx->maybeJSContext(),
+                                   bytes, length, inflated, &inflatedLength))
+        {
+            return NULL;
+        }
         return AtomizeAndCopyChars<CanGC>(cx, inflated, inflatedLength, ib);
     }
 
     jschar *tbcharsZ = InflateString(cx, bytes, &length);
     if (!tbcharsZ)
         return NULL;
     return AtomizeAndTakeOwnership(cx, tbcharsZ, length, ib);
 }
 
 template <AllowGC allowGC>
 JSAtom *
-js::AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
+js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     return AtomizeAndCopyChars<allowGC>(cx, chars, length, ib);
 }
 
 template JSAtom *
-js::AtomizeChars<CanGC>(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib);
+js::AtomizeChars<CanGC>(ExclusiveContext *cx,
+                        const jschar *chars, size_t length, InternBehavior ib);
 
 template JSAtom *
-js::AtomizeChars<NoGC>(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib);
+js::AtomizeChars<NoGC>(ExclusiveContext *cx,
+                       const jschar *chars, size_t length, InternBehavior ib);
 
 template <AllowGC allowGC>
 bool
-js::IndexToIdSlow(JSContext *cx, uint32_t index,
+js::IndexToIdSlow(ExclusiveContext *cx, uint32_t index,
                   typename MaybeRooted<jsid, allowGC>::MutableHandleType idp)
 {
     JS_ASSERT(index > JSID_INT_MAX);
 
     jschar buf[UINT32_CHAR_BUFFER_LENGTH];
     RangedPtr<jschar> end(ArrayEnd(buf), buf, ArrayEnd(buf));
     RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
 
@@ -422,24 +429,24 @@ js::IndexToIdSlow(JSContext *cx, uint32_
     if (!atom)
         return false;
 
     idp.set(JSID_FROM_BITS((size_t)atom));
     return true;
 }
 
 template bool
-js::IndexToIdSlow<CanGC>(JSContext *cx, uint32_t index, MutableHandleId idp);
+js::IndexToIdSlow<CanGC>(ExclusiveContext *cx, uint32_t index, MutableHandleId idp);
 
 template bool
-js::IndexToIdSlow<NoGC>(JSContext *cx, uint32_t index, FakeMutableHandle<jsid> idp);
+js::IndexToIdSlow<NoGC>(ExclusiveContext *cx, uint32_t index, FakeMutableHandle<jsid> idp);
 
 template <AllowGC allowGC>
 JSAtom *
-js::ToAtom(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v)
+js::ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v)
 {
     if (!v.isString()) {
         JSString *str = js::ToStringSlow<allowGC>(cx, v);
         if (!str)
             return NULL;
         JS::Anchor<JSString *> anchor(str);
         return AtomizeString<allowGC>(cx, str);
     }
@@ -448,20 +455,20 @@ js::ToAtom(JSContext *cx, typename Maybe
     if (str->isAtom())
         return &str->asAtom();
 
     JS::Anchor<JSString *> anchor(str);
     return AtomizeString<allowGC>(cx, str);
 }
 
 template JSAtom *
-js::ToAtom<CanGC>(JSContext *cx, HandleValue v);
+js::ToAtom<CanGC>(ExclusiveContext *cx, HandleValue v);
 
 template JSAtom *
-js::ToAtom<NoGC>(JSContext *cx, Value v);
+js::ToAtom<NoGC>(ExclusiveContext *cx, Value v);
 
 template<XDRMode mode>
 bool
 js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp)
 {
     if (mode == XDR_ENCODE) {
         uint32_t nchars = atomp->length();
         if (!xdr->codeUint32(&nchars))
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -40,26 +40,22 @@ struct JsidHasher
     static HashNumber hash(const Lookup &l) {
         return HashNumber(JSID_BITS(l));
     }
     static bool match(const jsid &id, const Lookup &l) {
         return id == l;
     }
 };
 
-} /* namespace js */
-
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
  * The lifetime of the result matches the lifetime of bytes.
  */
 extern const char *
-js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes);
-
-namespace js {
+AtomToPrintableString(ExclusiveContext *cx, JSAtom *atom, JSAutoByteString *bytes);
 
 /* Compute a hash function from chars/length. */
 inline uint32_t
 HashChars(const jschar *chars, size_t length)
 {
     uint32_t h = 0;
     for (; length; chars++, length--)
         h = JS_ROTATE_LEFT32(h, 4) ^ *chars;
@@ -210,31 +206,31 @@ FinishCommonNames(JSRuntime *rt);
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
     DoNotInternAtom = false,
     InternAtom = true
 };
 
 extern JSAtom *
-Atomize(JSContext *cx, const char *bytes, size_t length,
+Atomize(ExclusiveContext *cx, const char *bytes, size_t length,
         js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 extern JSAtom *
-AtomizeChars(JSContext *cx, const jschar *chars, size_t length,
+AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length,
              js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 extern JSAtom *
-AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
+AtomizeString(ExclusiveContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
 
 template <AllowGC allowGC>
 extern JSAtom *
-ToAtom(JSContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v);
+ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v);
 
 template<XDRMode mode>
 bool
 XDRAtom(XDRState<mode> *xdr, js::MutableHandleAtom atomp);
 
 } /* namespace js */
 
 #endif /* jsatom_h */
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -102,21 +102,21 @@ BackfillIndexInCharBuffer(uint32_t index
         index = next;
     } while (index > 0);
 
     return end;
 }
 
 template <AllowGC allowGC>
 bool
-IndexToIdSlow(JSContext *cx, uint32_t index,
+IndexToIdSlow(ExclusiveContext *cx, uint32_t index,
               typename MaybeRooted<jsid, allowGC>::MutableHandleType idp);
 
 inline bool
-IndexToId(JSContext *cx, uint32_t index, MutableHandleId idp)
+IndexToId(ExclusiveContext *cx, uint32_t index, MutableHandleId idp)
 {
     MaybeCheckStackRoots(cx);
 
     if (index <= JSID_INT_MAX) {
         idp.set(INT_TO_JSID(index));
         return true;
     }
 
@@ -189,21 +189,21 @@ TypeName(JSType type, JSRuntime *rt)
 
 inline Handle<PropertyName*>
 TypeName(JSType type, JSContext *cx)
 {
     return TypeName(type, cx->runtime());
 }
 
 inline Handle<PropertyName*>
-ClassName(JSProtoKey key, JSContext *cx)
+ClassName(JSProtoKey key, ExclusiveContext *cx)
 {
     JS_ASSERT(key < JSProto_LIMIT);
     JS_STATIC_ASSERT(offsetof(JSAtomState, Null) +
                      JSProto_LIMIT * sizeof(FixedHeapPtr<PropertyName>) <=
                      sizeof(JSAtomState));
     JS_STATIC_ASSERT(JSProto_Null == 0);
-    return (&cx->runtime()->atomState.Null)[key];
+    return (&cx->names().Null)[key];
 }
 
 } // namespace js
 
 #endif /* jsatominlines_h */
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -173,19 +173,19 @@ js_InitBooleanClass(JSContext *cx, Handl
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Boolean, ctor, booleanProto))
         return NULL;
 
     return booleanProto;
 }
 
 JSString *
-js_BooleanToString(JSContext *cx, JSBool b)
+js_BooleanToString(ExclusiveContext *cx, JSBool b)
 {
-    return b ? cx->runtime()->atomState.true_ : cx->runtime()->atomState.false_;
+    return b ? cx->names().true_ : cx->names().false_;
 }
 
 JS_PUBLIC_API(bool)
 js::ToBooleanSlow(const Value &v)
 {
     if (v.isString())
         return v.toString()->length() != 0;
 
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -11,17 +11,17 @@
  */
 
 #include "jsapi.h"
 
 extern JSObject *
 js_InitBooleanClass(JSContext *cx, js::HandleObject obj);
 
 extern JSString *
-js_BooleanToString(JSContext *cx, JSBool b);
+js_BooleanToString(js::ExclusiveContext *cx, JSBool b);
 
 namespace js {
 
 inline bool
 BooleanGetPrimitiveValue(HandleObject obj, JSContext *cx);
 
 } /* namespace js */
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -370,18 +370,22 @@ PopulateReportBlame(JSContext *cx, JSErr
  * allocates an error object, report and callstack. If code is running, simply
  * throw the static atom "out of memory". If code is not running, call the
  * error reporter directly.
  *
  * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does
  * not occur, so GC must be avoided or suppressed.
  */
 void
-js_ReportOutOfMemory(JSContext *cx)
+js_ReportOutOfMemory(ThreadSafeContext *cxArg)
 {
+    if (!cxArg->isJSContext())
+        return;
+    JSContext *cx = cxArg->asJSContext();
+
     cx->runtime()->hadOutOfMemory = true;
 
     if (JS_IsRunning(cx)) {
         cx->setPendingException(StringValue(cx->names().outOfMemory));
         return;
     }
 
     /* Get the message for this error, but we don't expand any arguments. */
@@ -417,22 +421,30 @@ js_ReportOverRecursed(JSContext *maybecx
      */
     fprintf(stderr, "js_ReportOverRecursed called\n");
 #endif
     if (maybecx)
         JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 }
 
 void
-js_ReportAllocationOverflow(JSContext *maybecx)
+js_ReportOverRecursed(ThreadSafeContext *cx)
 {
-    if (maybecx) {
-        AutoSuppressGC suppressGC(maybecx);
-        JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
-    }
+    js_ReportOverRecursed(cx->maybeJSContext());
+}
+
+void
+js_ReportAllocationOverflow(ThreadSafeContext *cxArg)
+{
+    if (!cxArg || !cxArg->isJSContext())
+        return;
+    JSContext *cx = cxArg->asJSContext();
+
+    AutoSuppressGC suppressGC(cx);
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
 }
 
 /*
  * Given flags and the state of cx, decide whether we should report an
  * error, a warning, or just continue execution normally.  Return
  * true if we should continue normally, without reporting anything;
  * otherwise, adjust *flags as appropriate and return false.
  */
@@ -1021,43 +1033,30 @@ js_HandleExecutionInterrupt(JSContext *c
 
 js::ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
   : ContextFriendFields(rt),
     contextKind_(kind),
     perThreadData(pt)
 { }
 
 bool
-ThreadSafeContext::isJSContext() const
-{
-    return contextKind_ == Context_JS;
-}
-
-JSContext *
-ThreadSafeContext::asJSContext()
-{
-    JS_ASSERT(isJSContext());
-    return reinterpret_cast<JSContext *>(this);
-}
-
-bool
 ThreadSafeContext::isForkJoinSlice() const
 {
     return contextKind_ == Context_ForkJoin;
 }
 
 ForkJoinSlice *
 ThreadSafeContext::asForkJoinSlice()
 {
     JS_ASSERT(isForkJoinSlice());
     return reinterpret_cast<ForkJoinSlice *>(this);
 }
 
 JSContext::JSContext(JSRuntime *rt)
-  : ThreadSafeContext(rt, &rt->mainThread, Context_JS),
+  : ExclusiveContext(rt, &rt->mainThread, Context_JS),
     throwing(false),
     exception(UndefinedValue()),
     options_(0),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     resolvingList(NULL),
     generatingError(false),
     enterCompartmentDepth_(0),
     savedFrameChains_(),
@@ -1079,23 +1078,16 @@ JSContext::JSContext(JSRuntime *rt)
     innermostGenerator_(NULL)
 {
 #ifdef DEBUG
     stackIterAssertionEnabled = true;
 #endif
 
     JS_ASSERT(static_cast<ContextFriendFields*>(this) ==
               ContextFriendFields::get(this));
-
-#ifdef JSGC_TRACK_EXACT_ROOTS
-    PodArrayZero(thingGCRooters);
-#endif
-#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
-    skipGCRooters = NULL;
-#endif
 }
 
 JSContext::~JSContext()
 {
     /* Free the stuff hanging off of cx. */
     JS_ASSERT(!resolvingList);
 }
 
@@ -1306,20 +1298,32 @@ JSContext::findVersion() const
 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
     : cx(cx)
 {
     JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
     cx->runtime()->assertValidThread();
     cx->runtime()->checkRequestDepth++;
 }
 
+JS::AutoCheckRequestDepth::AutoCheckRequestDepth(ContextFriendFields *cxArg)
+    : cx(static_cast<ThreadSafeContext *>(cxArg)->maybeJSContext())
+{
+    if (cx) {
+        JS_ASSERT(cx->runtime()->requestDepth || cx->runtime()->isHeapBusy());
+        cx->runtime()->assertValidThread();
+        cx->runtime()->checkRequestDepth++;
+    }
+}
+
 JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
 {
-    JS_ASSERT(cx->runtime()->checkRequestDepth != 0);
-    cx->runtime()->checkRequestDepth--;
+    if (cx) {
+        JS_ASSERT(cx->runtime()->checkRequestDepth != 0);
+        cx->runtime()->checkRequestDepth--;
+    }
 }
 
 #endif
 
 #ifdef JS_CRASH_DIAGNOSTICS
 void CompartmentChecker::check(StackFrame *fp)
 {
     if (fp)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -25,16 +25,27 @@
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
+struct DtoaState;
+
+extern void
+js_ReportOutOfMemory(js::ThreadSafeContext *cx);
+
+extern void
+js_ReportAllocationOverflow(js::ThreadSafeContext *cx);
+
+extern void
+js_ReportOverRecursed(js::ThreadSafeContext *cx);
+
 namespace js {
 
 struct CallsiteCloneKey {
     /* The original function that we are cloning. */
     JSFunction *original;
 
     /* The script of the call. */
     JSScript *script;
@@ -93,123 +104,233 @@ class AutoCycleDetector
 
 /* Updates references in the cycle detection set if the GC moves them. */
 extern void
 TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set);
 
 struct AutoResolving;
 
 class ForkJoinSlice;
+class RegExpCompartment;
+class DtoaCache;
 
 /*
- * ThreadSafeContext is the base class for both JSContext, the "normal"
- * sequential context, and ForkJoinSlice, the per-thread parallel context used
- * in PJS.
+ * Execution Context Overview:
+ *
+ * Several different structures may be used to provide a context for operations
+ * on the VM. Each context is thread local, but varies in what data it can
+ * access and what other threads may be running.
+ *
+ * - ThreadSafeContext is used by threads operating in one compartment which
+ * may run in parallel with other threads operating on the same or other
+ * compartments.
  *
- * When cast to a ThreadSafeContext, the only usable operations are casting
- * back to the context from which it came, and generic allocation
- * operations. These generic versions branch internally based on whether the
- * underneath context is really a JSContext or a ForkJoinSlice, and are in
- * general more expensive than using the context directly.
+ * - ExclusiveContext is used by threads operating in one compartment, where
+ * other threads may operate in other compartments, but *not* the one which
+ * the ExclusiveContext is in. A thread with an ExclusiveContext may enter the
+ * atoms compartment and atomize strings, in which case a lock is used.
  *
- * Thus, ThreadSafeContext should only be used for VM functions that may be
- * called in both sequential and parallel execution. The most likely class of
- * VM functions that do these are those that allocate commonly used data
- * structures, such as concatenating strings and extending elements.
+ * - JSContext is used only by the runtime's main thread. The context may
+ * operate in any compartment which is not locked by an ExclusiveContext or
+ * ThreadSafeContext, and will only run in parallel with threads using such
+ * contexts.
+ *
+ * An ExclusiveContext coerces to a ThreadSafeContext, and a JSContext coerces
+ * to an ExclusiveContext or ThreadSafeContext.
+ *
+ * Contexts which are a ThreadSafeContext but not an ExclusiveContext are used
+ * to represent a ForkJoinSlice, the per-thread parallel context used in PJS.
  */
-struct ThreadSafeContext : js::ContextFriendFields,
+
+struct ThreadSafeContext : ContextFriendFields,
                            public MallocProvider<ThreadSafeContext>
 {
   public:
     enum ContextKind {
         Context_JS,
+        Context_Exclusive,
         Context_ForkJoin
     };
 
   private:
     ContextKind contextKind_;
 
   public:
     PerThreadData *perThreadData;
 
-    explicit ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind);
+    ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind);
+
+    bool isJSContext() const {
+        return contextKind_ == Context_JS;
+    }
+
+    JSContext *maybeJSContext() const {
+        if (isJSContext())
+            return (JSContext *) this;
+        return NULL;
+    }
 
-    bool isJSContext() const;
-    JSContext *asJSContext();
+    JSContext *asJSContext() const {
+        // Note: there is no way to perform an unchecked coercion from a
+        // ThreadSafeContext to a JSContext. This ensures that trying to use
+        // the context as a JSContext off the main thread will NULL crash
+        // rather than race.
+        JS_ASSERT(isJSContext());
+        return maybeJSContext();
+    }
+
+    bool isExclusiveContext() const {
+        return contextKind_ == Context_JS || contextKind_ == Context_Exclusive;
+    }
+
+    ExclusiveContext *maybeExclusiveContext() const {
+        if (isExclusiveContext())
+            return (ExclusiveContext *) this;
+        return NULL;
+    }
+
+    ExclusiveContext *asExclusiveContext() const {
+        JS_ASSERT(isExclusiveContext());
+        return maybeExclusiveContext();
+    }
 
     bool isForkJoinSlice() const;
     ForkJoinSlice *asForkJoinSlice();
 
+    // The generational GC nursery may only be used on the main thread.
 #ifdef JSGC_GENERATIONAL
     inline bool hasNursery() const {
         return isJSContext();
     }
 
     inline js::Nursery &nursery() {
         JS_ASSERT(hasNursery());
         return runtime_->gcNursery;
     }
 #endif
 
-    /* Cut outs for string operations. */
-    StaticStrings &staticStrings() { return runtime_->staticStrings; }
-    JSAtomState &names() { return runtime_->atomState; }
-
     /*
      * Allocator used when allocating GCThings on this context. If we are a
-     * JSContext, this is the Zone allocator of the JSContext's zone. If we
-     * are the per-thread data of a ForkJoinSlice, this is a per-thread
-     * allocator.
+     * JSContext, this is the Zone allocator of the JSContext's zone.
+     * Otherwise, this is a per-thread allocator.
      *
      * This does not live in PerThreadData because the notion of an allocator
-     * is only per-thread in PJS. The runtime (and the main thread) can have
-     * more than one zone, each with its own allocator, and it's up to the
-     * context to specify what compartment and zone we are operating in.
+     * is only per-thread when off the main thread. The runtime (and the main
+     * thread) can have more than one zone, each with its own allocator, and
+     * it's up to the context to specify what compartment and zone we are
+     * operating in.
      */
   protected:
     Allocator *allocator_;
 
   public:
     static size_t offsetOfAllocator() { return offsetof(ThreadSafeContext, allocator_); }
 
     inline Allocator *const allocator();
 
-    /* GC support. */
-    AllowGC allowGC() const {
-        switch (contextKind_) {
-          case Context_JS:
-            return CanGC;
-          case Context_ForkJoin:
-            return NoGC;
-          default:
-            /* Silence warnings. */
-            MOZ_ASSUME_UNREACHABLE("Bad context kind");
-        }
-    }
+    // Allocations can only trigger GC when running on the main thread.
+    inline AllowGC allowGC() const { return isJSContext() ? CanGC : NoGC; }
 
     template <typename T>
     bool isInsideCurrentZone(T thing) const {
         return thing->isInsideZone(zone_);
     }
 
-    void *onOutOfMemory(void *p, size_t nbytes) {
-        return runtime_->onOutOfMemory(p, nbytes, isJSContext() ? asJSContext() : NULL);
+    template <typename T>
+    inline bool isInsideCurrentCompartment(T thing) const {
+        return thing->compartment() == compartment_;
     }
+
+    void *onOutOfMemory(void *p, size_t nbytes) {
+        return runtime_->onOutOfMemory(p, nbytes, maybeJSContext());
+    }
+
     inline void updateMallocCounter(size_t nbytes) {
-        /* Note: this is racy. */
+        // Note: this is racy.
         runtime_->updateMallocCounter(zone_, nbytes);
     }
+
     void reportAllocationOverflow() {
-        js_ReportAllocationOverflow(isJSContext() ? asJSContext() : NULL);
+        js_ReportAllocationOverflow(this);
+    }
+
+    // Builtin atoms are immutable and may be accessed freely from any thread.
+    JSAtomState &names() { return runtime_->atomState; }
+    StaticStrings &staticStrings() { return runtime_->staticStrings; }
+    PropertyName *emptyString() { return runtime_->emptyString; }
+
+    // GCs cannot happen while non-main threads are running.
+    uint64_t gcNumber() { return runtime_->gcNumber; }
+    bool isHeapBusy() { return runtime_->isHeapBusy(); }
+
+    // Thread local data that may be accessed freely.
+    DtoaState *dtoaState() {
+        return perThreadData->dtoaState;
     }
 };
 
+class ExclusiveContext : public ThreadSafeContext
+{
+    friend class gc::ArenaLists;
+    friend class CompartmentChecker;
+    friend class AutoEnterAtomsCompartment;
+    friend struct StackBaseShape;
+    friend void JSScript::initCompartmentAndPrincipals(ExclusiveContext *cx,
+                                                       const JS::CompileOptions &options);
+
+    inline void privateSetCompartment(JSCompartment *comp);
+
+  public:
+
+    ExclusiveContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
+      : ThreadSafeContext(rt, pt, kind)
+    {}
+
+    inline bool typeInferenceEnabled() const;
+
+    // Per compartment data that can be accessed freely from an ExclusiveContext.
+    inline RegExpCompartment &regExps();
+    inline RegExpStatics *regExpStatics();
+    inline PropertyTree &propertyTree();
+    inline BaseShapeSet &baseShapes();
+    inline InitialShapeSet &initialShapes();
+    inline DtoaCache &dtoaCache();
+    types::TypeObject *getNewType(Class *clasp, TaggedProto proto, JSFunction *fun = NULL);
+
+    // Current global. This is only safe to use within the scope of the
+    // AutoCompartment from which it's called.
+    inline js::Handle<js::GlobalObject*> global() const;
+
+    // Methods to access runtime wide data that must be protected by locks.
+
+    frontend::ParseMapPool &parseMapPool() {
+        runtime_->assertValidThread();
+        return runtime_->parseMapPool;
+    }
+
+    AtomSet &atoms() {
+        runtime_->assertValidThread();
+        return runtime_->atoms;
+    }
+
+    ScriptDataTable &scriptDataTable() {
+        runtime_->assertValidThread();
+        return runtime_->scriptDataTable;
+    }
+};
+
+inline void
+MaybeCheckStackRoots(ExclusiveContext *cx)
+{
+    MaybeCheckStackRoots(cx->maybeJSContext());
+}
+
 } /* namespace js */
 
-struct JSContext : js::ThreadSafeContext,
+struct JSContext : public js::ExclusiveContext,
                    public mozilla::LinkedListElement<JSContext>
 {
     explicit JSContext(JSRuntime *rt);
     ~JSContext();
 
     JSRuntime *runtime() const { return runtime_; }
     JSCompartment *compartment() const { return compartment_; }
 
@@ -289,40 +410,32 @@ struct JSContext : js::ThreadSafeContext
      */
   private:
     JSObject *defaultCompartmentObject_;
   public:
     inline void setDefaultCompartmentObject(JSObject *obj);
     inline void setDefaultCompartmentObjectIfUnset(JSObject *obj);
     JSObject *maybeDefaultCompartmentObject() const { return defaultCompartmentObject_; }
 
-    /*
-     * Current global. This is only safe to use within the scope of the
-     * AutoCompartment from which it's called.
-     */
-    inline js::Handle<js::GlobalObject*> global() const;
-
     /* Wrap cx->exception for the current compartment. */
     void wrapPendingException();
 
     /* State for object and array toSource conversion. */
     js::ObjectSet       cycleDetectorSet;
 
     /* Per-context optional error reporter. */
     JSErrorReporter     errorReporter;
 
     /* Branch callback. */
     JSOperationCallback operationCallback;
 
     /* Client opaque pointers. */
     void                *data;
     void                *data2;
 
-    inline js::RegExpStatics *regExpStatics();
-
   public:
 
     /*
      * Return:
      * - The newest scripted frame's version, if there is such a frame.
      * - The version from the compartment.
      * - The default version.
      *
@@ -344,34 +457,30 @@ struct JSContext : js::ThreadSafeContext
 
     bool hasExtraWarningsOption() const { return hasOption(JSOPTION_EXTRA_WARNINGS); }
     bool hasWErrorOption() const { return hasOption(JSOPTION_WERROR); }
 
     js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; }
     inline js::LifoAlloc &analysisLifoAlloc();
     inline js::LifoAlloc &typeLifoAlloc();
 
-    inline js::PropertyTree &propertyTree();
-
 #ifdef JS_THREADSAFE
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
 #endif
 
     /* Stored here to avoid passing it around as a parameter. */
     unsigned               resolveFlags;
 
     /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */
     js::Value           iterValue;
 
     bool jitIsBroken;
 
-    inline bool typeInferenceEnabled() const;
-
     void updateJITEnabled();
 
     /* Whether this context has JS frames on the stack. */
     bool currentlyRunning() const;
 
     bool currentlyRunningInInterpreter() const {
         return mainThread().activation()->isInterpreter();
     }
@@ -599,23 +708,23 @@ extern JSBool
 js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
                        void *userRef, const unsigned errorNumber,
                        js::ErrorArgumentsType argumentsType, va_list ap);
 
 extern bool
 js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
                             void *userRef, const unsigned errorNumber,
                             const jschar **args);
+#endif
 
 extern JSBool
 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
                         void *userRef, const unsigned errorNumber,
                         char **message, JSErrorReport *reportp,
                         js::ErrorArgumentsType argumentsType, va_list ap);
-#endif
 
 namespace js {
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 extern void
 ReportUsageError(JSContext *cx, HandleObject callee, const char *msg);
 
 /*
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -25,22 +25,22 @@
 
 #include "vm/ObjectImpl-inl.h"
 
 namespace js {
 
 #ifdef JS_CRASH_DIAGNOSTICS
 class CompartmentChecker
 {
-    JSContext *context;
+    ExclusiveContext *context;
     JSCompartment *compartment;
 
   public:
-    explicit CompartmentChecker(JSContext *cx)
-      : context(cx), compartment(cx->compartment())
+    explicit CompartmentChecker(ExclusiveContext *cx)
+      : context(cx), compartment(cx->compartment_)
     {}
 
     /*
      * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
      * compartment mismatches.
      */
     static void fail(JSCompartment *c1, JSCompartment *c2) {
         printf("*** Compartment mismatch %p vs. %p\n", (void *) c1, (void *) c2);
@@ -56,17 +56,17 @@ class CompartmentChecker
     static void check(JSCompartment *c1, JSCompartment *c2) {
         JS_ASSERT(c1 != c1->rt->atomsCompartment);
         JS_ASSERT(c2 != c2->rt->atomsCompartment);
         if (c1 != c2)
             fail(c1, c2);
     }
 
     void check(JSCompartment *c) {
-        if (c && c != context->runtime()->atomsCompartment) {
+        if (c && c != compartment->rt->atomsCompartment) {
             if (!compartment)
                 compartment = c;
             else if (c != compartment)
                 fail(compartment, c);
         }
     }
 
     void checkZone(JS::Zone *z) {
@@ -135,74 +135,75 @@ class CompartmentChecker
 };
 #endif /* JS_CRASH_DIAGNOSTICS */
 
 /*
  * Don't perform these checks when called from a finalizer. The checking
  * depends on other objects not having been swept yet.
  */
 #define START_ASSERT_SAME_COMPARTMENT()                                       \
-    JS_ASSERT(cx->compartment()->zone() == cx->zone());                       \
-    if (cx->runtime()->isHeapBusy())                                          \
+    if (cx->isHeapBusy())                                                     \
         return;                                                               \
     CompartmentChecker c(cx)
 
 template <class T1> inline void
-assertSameCompartment(JSContext *cx, const T1 &t1)
+assertSameCompartment(ExclusiveContext *cx, const T1 &t1)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
 #endif
 }
 
 template <class T1> inline void
-assertSameCompartmentDebugOnly(JSContext *cx, const T1 &t1)
+assertSameCompartmentDebugOnly(ExclusiveContext *cx, const T1 &t1)
 {
 #ifdef DEBUG
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
 #endif
 }
 
 template <class T1, class T2> inline void
-assertSameCompartment(JSContext *cx, const T1 &t1, const T2 &t2)
+assertSameCompartment(ExclusiveContext *cx, const T1 &t1, const T2 &t2)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
     c.check(t2);
 #endif
 }
 
 template <class T1, class T2, class T3> inline void
-assertSameCompartment(JSContext *cx, const T1 &t1, const T2 &t2, const T3 &t3)
+assertSameCompartment(ExclusiveContext *cx, const T1 &t1, const T2 &t2, const T3 &t3)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
     c.check(t2);
     c.check(t3);
 #endif
 }
 
 template <class T1, class T2, class T3, class T4> inline void
-assertSameCompartment(JSContext *cx, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4)
+assertSameCompartment(ExclusiveContext *cx,
+                      const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
     c.check(t2);
     c.check(t3);
     c.check(t4);
 #endif
 }
 
 template <class T1, class T2, class T3, class T4, class T5> inline void
-assertSameCompartment(JSContext *cx, const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5)
+assertSameCompartment(ExclusiveContext *cx,
+                      const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     START_ASSERT_SAME_COMPARTMENT();
     c.check(t1);
     c.check(t2);
     c.check(t3);
     c.check(t4);
     c.check(t5);
@@ -333,16 +334,52 @@ CallSetter(JSContext *cx, HandleObject o
     if (!(attrs & JSPROP_SHORTID))
         return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
 
     RootedId nid(cx, INT_TO_JSID(shortid));
 
     return CallJSPropertyOpSetter(cx, op, obj, nid, strict, vp);
 }
 
+inline uintptr_t
+GetNativeStackLimit(ExclusiveContext *cx)
+{
+    return GetNativeStackLimit(cx->asJSContext()->runtime());
+}
+
+inline RegExpCompartment &
+ExclusiveContext::regExps()
+{
+    return compartment_->regExps;
+}
+
+inline PropertyTree&
+ExclusiveContext::propertyTree()
+{
+    return compartment_->propertyTree;
+}
+
+inline BaseShapeSet &
+ExclusiveContext::baseShapes()
+{
+    return compartment_->baseShapes;
+}
+
+inline InitialShapeSet &
+ExclusiveContext::initialShapes()
+{
+    return compartment_->initialShapes;
+}
+
+inline DtoaCache &
+ExclusiveContext::dtoaCache()
+{
+    return compartment_->dtoaCache;
+}
+
 }  /* namespace js */
 
 inline js::LifoAlloc &
 JSContext::analysisLifoAlloc()
 {
     return compartment()->analysisLifoAlloc;
 }
 
@@ -355,22 +392,16 @@ JSContext::typeLifoAlloc()
 inline void
 JSContext::setPendingException(js::Value v) {
     JS_ASSERT(!IsPoisonedValue(v));
     this->throwing = true;
     this->exception = v;
     js::assertSameCompartment(this, v);
 }
 
-inline js::PropertyTree&
-JSContext::propertyTree()
-{
-    return compartment()->propertyTree;
-}
-
 inline void
 JSContext::setDefaultCompartmentObject(JSObject *obj)
 {
     defaultCompartmentObject_ = obj;
 
     if (!hasEnteredCompartment()) {
         /*
          * If JSAPI callers want to JS_SetGlobalObject while code is running,
@@ -430,16 +461,28 @@ JSContext::leaveCompartment(JSCompartmen
 inline void
 JSContext::setCompartment(JSCompartment *comp)
 {
     compartment_ = comp;
     zone_ = comp ? comp->zone() : NULL;
     allocator_ = zone_ ? &zone_->allocator : NULL;
 }
 
+inline void
+js::ExclusiveContext::privateSetCompartment(JSCompartment *comp)
+{
+    if (isJSContext()) {
+        asJSContext()->setCompartment(comp);
+    } else {
+        compartment_ = comp;
+        if (zone_ != comp->zone())
+            MOZ_CRASH();
+    }
+}
+
 inline JSScript *
 JSContext::currentScript(jsbytecode **ppc,
                          MaybeAllowCrossCompartment allowCrossCompartment) const
 {
     if (ppc)
         *ppc = NULL;
 
     js::Activation *act = mainThread().activation();
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -130,16 +130,17 @@ struct JSCompartment
     bool                         isSystem;
     bool                         marked;
 
     void mark() { marked = true; }
 
   private:
     friend struct JSRuntime;
     friend struct JSContext;
+    friend class js::ExclusiveContext;
     js::ReadBarriered<js::GlobalObject> global_;
 
     unsigned                     enterCompartmentDepth;
 
   public:
     void enter() { enterCompartmentDepth++; }
     void leave() { enterCompartmentDepth--; }
 
@@ -220,19 +221,16 @@ struct JSCompartment
     void sweepInitialShapeTable();
     void markAllInitialShapeTableEntries(JSTracer *trc);
 
     /* Set of default 'new' or lazy types in the compartment. */
     js::types::TypeObjectSet     newTypeObjects;
     js::types::TypeObjectSet     lazyTypeObjects;
     void sweepNewTypeObjectTable(js::types::TypeObjectSet &table);
 
-    js::types::TypeObject *getNewType(JSContext *cx, js::Class *clasp, js::TaggedProto proto,
-                                      JSFunction *fun = NULL);
-
     js::types::TypeObject *getLazyType(JSContext *cx, js::Class *clasp, js::TaggedProto proto);
 
     /*
      * Hash table of all manually call site-cloned functions from within
      * self-hosted code. Cloning according to call site provides extra
      * sensitivity for type specialization and inlining.
      */
     js::CallsiteCloneTable callsiteClones;
@@ -406,36 +404,40 @@ class js::AutoDebugModeGC
 
     void scheduleGC(Zone *zone) {
         JS_ASSERT(!rt->isHeapBusy());
         PrepareZoneForGC(zone);
         needGC = true;
     }
 };
 
+namespace js {
+
 inline bool
-JSContext::typeInferenceEnabled() const
+ExclusiveContext::typeInferenceEnabled() const
 {
-    return compartment()->zone()->types.inferenceEnabled;
+    // Type inference cannot be enabled in compartments which are accessed off
+    // the main thread by an ExclusiveContext. TI data is stored in per-zone
+    // allocators which could otherwise race with main thread operations.
+    JS_ASSERT_IF(!isJSContext(), !compartment_->zone()->types.inferenceEnabled);
+    return compartment_->zone()->types.inferenceEnabled;
 }
 
 inline js::Handle<js::GlobalObject*>
-JSContext::global() const
+ExclusiveContext::global() const
 {
     /*
      * It's safe to use |unsafeGet()| here because any compartment that is
      * on-stack will be marked automatically, so there's no need for a read
      * barrier on it. Once the compartment is popped, the handle is no longer
      * safe to use.
      */
-    return js::Handle<js::GlobalObject*>::fromMarkedLocation(compartment()->global_.unsafeGet());
+    return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
 }
 
-namespace js {
-
 class AssertCompartmentUnchanged
 {
   public:
     AssertCompartmentUnchanged(JSContext *cx
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : cx(cx), oldCompartment(cx->compartment())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -36,36 +36,36 @@ js::AutoCompartment::AutoCompartment(JSC
 js::AutoCompartment::~AutoCompartment()
 {
     cx_->leaveCompartment(origin_);
 }
 
 namespace js {
 
 /*
- * Entering the atoms comaprtment is not possible with the AutoCompartment
+ * Entering the atoms compartment is not possible with AutoCompartment
  * since the atoms compartment does not have a global.
  *
  * Note: since most of the VM assumes that cx->global is non-null, only a
  * restricted set of (atom creating/destroying) operations may be used from
  * inside the atoms compartment.
  */
 class AutoEnterAtomsCompartment
 {
-    JSContext *cx;
+    ExclusiveContext *cx;
     JSCompartment *oldCompartment;
   public:
-    AutoEnterAtomsCompartment(JSContext *cx)
+    AutoEnterAtomsCompartment(ExclusiveContext *cx)
       : cx(cx),
-        oldCompartment(cx->compartment())
+        oldCompartment(cx->compartment_)
     {
-        cx->setCompartment(cx->runtime()->atomsCompartment);
+        cx->privateSetCompartment(cx->runtime_->atomsCompartment);
     }
 
     ~AutoEnterAtomsCompartment()
     {
-        cx->setCompartment(oldCompartment);
+        cx->privateSetCompartment(oldCompartment);
     }
 };
 
 } /* namespace js */
 
 #endif /* jscompartmentinlines_h */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -39,16 +39,17 @@
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
+#include "jsstrinlines.h"
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -25,16 +25,17 @@
 
 #include "gc/Marking.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/StringBuffer.h"
 
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
+#include "jsstrinlines.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 using mozilla::PodArrayZero;
 using mozilla::PodZero;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1123,21 +1123,25 @@ js_DefineOwnProperty(JSContext *cx, JSOb
 }
 
 JS_FRIEND_API(JSBool)
 js_ReportIsNotFunction(JSContext *cx, const JS::Value& v)
 {
     return ReportIsNotFunction(cx, v);
 }
 
-#if defined(DEBUG) && defined(JS_THREADSAFE)
+#ifdef DEBUG
 JS_PUBLIC_API(bool)
 js::IsInRequest(JSContext *cx)
 {
+#ifdef JS_THREADSAFE
     return !!cx->runtime()->requestDepth;
+#else
+    return true;
+#endif
 }
 #endif
 
 #ifdef JSGC_GENERATIONAL
 JS_FRIEND_API(void)
 JS_StorePostBarrierCallback(JSContext* cx, void (*callback)(JSTracer *trc, void *key), void *key)
 {
     cx->runtime()->gcStoreBuffer.putCallback(callback, key);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -614,45 +614,51 @@ IsObjectInContextCompartment(JSObject *o
 #define JSITER_FOR_OF     0x20  /* harmony for-of loop */
 
 inline uintptr_t
 GetNativeStackLimit(const JSRuntime *rt)
 {
     return PerThreadDataFriendFields::getMainThread(rt)->nativeStackLimit;
 }
 
+inline uintptr_t
+GetNativeStackLimit(JSContext *cx)
+{
+    return GetNativeStackLimit(GetRuntime(cx));
+}
+
 /*
  * These macros report a stack overflow and run |onerror| if we are close to
  * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a little
  * extra space so that we can ensure that crucial code is able to run.
  */
 
 #define JS_CHECK_RECURSION(cx, onerror)                              \
     JS_BEGIN_MACRO                                                              \
         int stackDummy_;                                                        \
-        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(js::GetRuntime(cx)), &stackDummy_)) { \
+        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_)) {  \
             js_ReportOverRecursed(cx);                                          \
             onerror;                                                            \
         }                                                                       \
     JS_END_MACRO
 
 #define JS_CHECK_RECURSION_WITH_EXTRA_DONT_REPORT(cx, extra, onerror)           \
     JS_BEGIN_MACRO                                                              \
         uint8_t stackDummy_;                                                    \
-        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(js::GetRuntime(cx)),   \
+        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx),                   \
                                  &stackDummy_ - (extra)))                       \
         {                                                                       \
             onerror;                                                            \
         }                                                                       \
     JS_END_MACRO
 
 #define JS_CHECK_CHROME_RECURSION(cx, onerror)                                  \
     JS_BEGIN_MACRO                                                              \
         int stackDummy_;                                                        \
-        if (!JS_CHECK_STACK_SIZE_WITH_TOLERANCE(js::GetNativeStackLimit(js::GetRuntime(cx)), \
+        if (!JS_CHECK_STACK_SIZE_WITH_TOLERANCE(js::GetNativeStackLimit(cx),    \
                                                 &stackDummy_,                   \
                                                 1024 * sizeof(size_t)))         \
         {                                                                       \
             js_ReportOverRecursed(cx);                                          \
             onerror;                                                            \
         }                                                                       \
     JS_END_MACRO
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1529,17 +1529,17 @@ js::Function(JSContext *cx, unsigned arg
 
 bool
 JSFunction::isBuiltinFunctionConstructor()
 {
     return maybeNative() == Function;
 }
 
 JSFunction *
-js::NewFunction(JSContext *cx, HandleObject funobjArg, Native native, unsigned nargs,
+js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, unsigned nargs,
                 JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
                 gc::AllocKind allocKind /* = JSFunction::FinalizeKind */,
                 NewObjectKind newKind /* = GenericObject */)
 {
     JS_ASSERT(allocKind == JSFunction::FinalizeKind || allocKind == JSFunction::ExtendedFinalizeKind);
     JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind));
     JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind));
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -351,17 +351,17 @@ class JSFunction : public JSObject
      * initExtendedSlot.
      */
     inline void initializeExtended();
     inline void initExtendedSlot(size_t which, const js::Value &val);
     inline void setExtendedSlot(size_t which, const js::Value &val);
     inline const js::Value &getExtendedSlot(size_t which) const;
 
     /* Constructs a new type for the function if necessary. */
-    static bool setTypeForScriptedFunction(JSContext *cx, js::HandleFunction fun,
+    static bool setTypeForScriptedFunction(js::ExclusiveContext *cx, js::HandleFunction fun,
                                            bool singleton = false);
 
     /* GC support. */
     js::gc::AllocKind getAllocKind() const {
         js::gc::AllocKind kind = FinalizeKind;
         if (isExtended())
             kind = ExtendedFinalizeKind;
         JS_ASSERT_IF(isTenured(), kind == tenuredGetAllocKind());
@@ -381,17 +381,17 @@ JSAPIToJSFunctionFlags(unsigned flags)
 }
 
 namespace js {
 
 extern JSBool
 Function(JSContext *cx, unsigned argc, Value *vp);
 
 extern JSFunction *
-NewFunction(JSContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
+NewFunction(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
             JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
             gc::AllocKind allocKind = JSFunction::FinalizeKind,
             NewObjectKind newKind = GenericObject);
 
 extern JSFunction *
 DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = JSFunction::FinalizeKind,
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -388,17 +388,17 @@ TryNewNurseryGCThing(ThreadSafeContext *
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
 template <typename T, AllowGC allowGC>
 inline T *
-NewGCThing(js::ThreadSafeContext *tcx, AllocKind kind, size_t thingSize, InitialHeap heap)
+NewGCThing(ThreadSafeContext *tcx, AllocKind kind, size_t thingSize, InitialHeap heap)
 {
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
 
     if (tcx->isJSContext()) {
         JSContext *cx = tcx->asJSContext();
         JS_ASSERT_IF(cx->compartment() == cx->runtime()->atomsCompartment,
                      kind == FINALIZE_STRING ||
                      kind == FINALIZE_SHORT_STRING ||
@@ -413,17 +413,17 @@ NewGCThing(js::ThreadSafeContext *tcx, A
         if (cx->runtime()->needZealousGC() && allowGC)
             js::gc::RunDebugGC(cx);
 #endif
 
         if (allowGC)
             MaybeCheckStackRoots(cx);
     }
 
-#if defined(JSGC_GENERATIONAL)
+#ifdef JSGC_GENERATIONAL
     if (tcx->hasNursery() && ShouldNurseryAllocate(tcx->nursery(), kind, heap)) {
         T *t = TryNewNurseryGCThing<T, allowGC>(tcx, thingSize);
         if (t)
             return t;
     }
 #endif
 
     T *t = static_cast<T *>(tcx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2333,19 +2333,19 @@ TypeZone::init(JSContext *cx)
     {
         return;
     }
 
     inferenceEnabled = true;
 }
 
 TypeObject *
-TypeCompartment::newTypeObject(JSContext *cx, Class *clasp, Handle<TaggedProto> proto, bool unknown)
-{
-    JS_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
+TypeCompartment::newTypeObject(ExclusiveContext *cx, Class *clasp, Handle<TaggedProto> proto, bool unknown)
+{
+    JS_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
 
     TypeObject *object = gc::NewGCThing<TypeObject, CanGC>(cx, gc::FINALIZE_TYPE_OBJECT,
                                                            sizeof(TypeObject), gc::TenuredHeap);
     if (!object)
         return NULL;
     new(object) TypeObject(clasp, proto, clasp == &JSFunction::class_, unknown);
 
     if (!cx->typeInferenceEnabled())
@@ -5874,21 +5874,24 @@ JSScript::makeAnalysis(JSContext *cx)
         self->types->analysis = NULL;
         return false;
     }
 
     return true;
 }
 
 /* static */ bool
-JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */)
-{
-    if (!cx->typeInferenceEnabled())
+JSFunction::setTypeForScriptedFunction(ExclusiveContext *cxArg, HandleFunction fun,
+                                       bool singleton /* = false */)
+{
+    if (!cxArg->typeInferenceEnabled())
         return true;
 
+    JSContext *cx = cxArg->asJSContext();
+
     if (singleton) {
         if (!setSingletonType(cx, fun))
             return false;
     } else {
         RootedObject funProto(cx, fun->getProto());
         TypeObject *type =
             cx->compartment()->types.newTypeObject(cx, &JSFunction::class_, funProto);
         if (!type)
@@ -5949,17 +5952,17 @@ JSObject::splicePrototype(JSContext *cx,
     Rooted<TypeObject*> protoType(cx, NULL);
     if (proto.isObject()) {
         protoType = proto.toObject()->getType(cx);
         if (!protoType)
             return false;
     }
 
     if (!cx->typeInferenceEnabled()) {
-        TypeObject *type = cx->compartment()->getNewType(cx, clasp, proto);
+        TypeObject *type = cx->getNewType(clasp, proto);
         if (!type)
             return false;
         self->type_ = type;
         return true;
     }
 
     type->clasp = clasp;
     type->proto = proto.raw();
@@ -6087,20 +6090,22 @@ JSObject::setNewTypeUnknown(JSContext *c
         if (TypeObjectSet::Ptr p = table.lookup(TypeObjectSet::Lookup(clasp, obj.get())))
             MarkTypeObjectUnknownProperties(cx, *p);
     }
 
     return true;
 }
 
 TypeObject *
-JSCompartment::getNewType(JSContext *cx, Class *clasp, TaggedProto proto_, JSFunction *fun_)
+ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_)
 {
     JS_ASSERT_IF(fun_, proto_.isObject());
-    JS_ASSERT_IF(proto_.isObject(), cx->compartment() == proto_.toObject()->compartment());
+    JS_ASSERT_IF(proto_.isObject(), isInsideCurrentCompartment(proto_.toObject()));
+
+    TypeObjectSet &newTypeObjects = compartment_->newTypeObjects;
 
     if (!newTypeObjects.initialized() && !newTypeObjects.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = newTypeObjects.lookupForAdd(TypeObjectSet::Lookup(clasp, proto_));
     if (p) {
         TypeObject *type = *p;
 
@@ -6111,42 +6116,43 @@ JSCompartment::getNewType(JSContext *cx,
          * we must clear the new script information from the type and will not
          * be able to assume any definite properties for instances of the type.
          * This case is rare, but can happen if, for example, two scripted
          * functions have the same value for their 'prototype' property, or if
          * Object.create is called with a prototype object that is also the
          * 'prototype' property of some scripted function.
          */
         if (type->newScript && type->newScript->fun != fun_)
-            type->clearNewScript(cx);
+            type->clearNewScript(asJSContext());
 
         return type;
     }
 
-    Rooted<TaggedProto> proto(cx, proto_);
-    RootedFunction fun(cx, fun_);
-
-    if (proto.isObject() && !proto.toObject()->setDelegate(cx))
+    Rooted<TaggedProto> proto(this, proto_);
+    RootedFunction fun(this, fun_);
+
+    if (proto.isObject() && !proto.toObject()->setDelegate(this))
         return NULL;
 
     bool markUnknown =
         proto.isObject()
         ? proto.toObject()->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN)
         : true;
 
-    RootedTypeObject type(cx, types.newTypeObject(cx, clasp, proto, markUnknown));
+    RootedTypeObject type(this, compartment_->types.newTypeObject(this, clasp, proto, markUnknown));
     if (!type)
         return NULL;
 
     if (!newTypeObjects.relookupOrAdd(p, TypeObjectSet::Lookup(clasp, proto), type.get()))
         return NULL;
 
-    if (!cx->typeInferenceEnabled())
+    if (!typeInferenceEnabled())
         return type;
 
+    JSContext *cx = asJSContext();
     AutoEnterAnalysis enter(cx);
 
     /*
      * Set the special equality flag for types whose prototype also has the
      * flag set. This is a hack, :XXX: need a real correspondence between
      * types and the possible js::Class of objects with that type.
      */
     if (proto.isObject()) {
@@ -6178,22 +6184,16 @@ JSCompartment::getNewType(JSContext *cx,
      */
     if (type->unknownProperties())
         type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
 
     return type;
 }
 
 TypeObject *
-JSObject::getNewType(JSContext *cx, Class *clasp, JSFunction *fun)
-{
-    return cx->compartment()->getNewType(cx, clasp, this, fun);
-}
-
-TypeObject *
 JSCompartment::getLazyType(JSContext *cx, Class *clasp, TaggedProto proto)
 {
     JS_ASSERT(cx->compartment() == this);
     JS_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
 
     AutoEnterAnalysis enter(cx);
 
     TypeObjectSet &table = cx->compartment()->lazyTypeObjects;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1407,17 +1407,17 @@ struct TypeCompartment
     void print(JSContext *cx, bool force);
 
     /*
      * Make a function or non-function object associated with an optional
      * script. The 'key' parameter here may be an array, typed array, function
      * or JSProto_Object to indicate a type whose class is unknown (not just
      * js_ObjectClass).
      */
-    TypeObject *newTypeObject(JSContext *cx, Class *clasp, Handle<TaggedProto> proto,
+    TypeObject *newTypeObject(ExclusiveContext *cx, Class *clasp, Handle<TaggedProto> proto,
                               bool unknown = false);
 
     /* Get or make an object for an allocation site, and add to the allocation site table. */
     TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
 
     void processPendingRecompiles(FreeOp *fop);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -504,17 +504,17 @@ GetClassForProtoKey(JSProtoKey key)
  * active global.
  */
 inline TypeObject *
 GetTypeNewObject(JSContext *cx, JSProtoKey key)
 {
     RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto))
         return NULL;
-    return proto->getNewType(cx, GetClassForProtoKey(key));
+    return cx->getNewType(GetClassForProtoKey(key), proto.get());
 }
 
 /* Get a type object for the immediate allocation site within a native. */
 inline TypeObject *
 GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
 {
     if (cx->typeInferenceEnabled()) {
         jsbytecode *pc;
@@ -551,22 +551,22 @@ TypeMonitorCall(JSContext *cx, const js:
     if (args.callee().is<JSFunction>()) {
         JSFunction *fun = &args.callee().as<JSFunction>();
         if (fun->isInterpreted() && fun->nonLazyScript()->types && cx->typeInferenceEnabled())
             TypeMonitorCallSlow(cx, &args.callee(), args, constructing);
     }
 }
 
 inline bool
-TrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
+TrackPropertyTypes(ExclusiveContext *cx, JSObject *obj, jsid id)
 {
     if (!cx->typeInferenceEnabled() || obj->hasLazyType() || obj->type()->unknownProperties())
         return false;
 
-    if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id, cx))
+    if (obj->hasSingletonType() && !obj->type()->maybeGetProperty(id, cx->asJSContext()))
         return false;
 
     return true;
 }
 
 inline void
 EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
 {
@@ -582,31 +582,33 @@ EnsureTrackPropertyTypes(JSContext *cx, 
         obj->type()->getProperty(cx, id, true);
     }
 
     JS_ASSERT(obj->type()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
 }
 
 /* Add a possible type for a property of obj. */
 inline void
-AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, Type type)
+AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, Type type)
 {
-    if (cx->typeInferenceEnabled())
+    if (cx->typeInferenceEnabled()) {
         id = IdToTypeId(id);
-    if (TrackPropertyTypes(cx, obj, id))
-        obj->type()->addPropertyType(cx, id, type);
+        if (TrackPropertyTypes(cx, obj, id))
+            obj->type()->addPropertyType(cx->asJSContext(), id, type);
+    }
 }
 
 inline void
-AddTypePropertyId(JSContext *cx, JSObject *obj, jsid id, const Value &value)
+AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &value)
 {
-    if (cx->typeInferenceEnabled())
+    if (cx->typeInferenceEnabled()) {
         id = IdToTypeId(id);
-    if (TrackPropertyTypes(cx, obj, id))
-        obj->type()->addPropertyType(cx, id, value);
+        if (TrackPropertyTypes(cx, obj, id))
+            obj->type()->addPropertyType(cx->asJSContext(), id, value);
+    }
 }
 
 inline void
 AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, Type type)
 {
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, type);
 }
@@ -615,20 +617,20 @@ inline void
 AddTypeProperty(JSContext *cx, TypeObject *obj, const char *name, const Value &value)
 {
     if (cx->typeInferenceEnabled() && !obj->unknownProperties())
         obj->addPropertyType(cx, name, value);
 }
 
 /* Set one or more dynamic flags on a type object. */
 inline void
-MarkTypeObjectFlags(JSContext *cx, JSObject *obj, TypeObjectFlags flags)
+MarkTypeObjectFlags(ExclusiveContext *cx, JSObject *obj, TypeObjectFlags flags)
 {
     if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->hasAllFlags(flags))
-        obj->type()->setFlags(cx, flags);
+        obj->type()->setFlags(cx->asJSContext(), flags);
 }
 
 /*
  * Mark all properties of a type object as unknown. If markSetsUnknown is set,
  * scan the entire compartment and mark all type sets containing it as having
  * an unknown object. This is needed for correctness in dealing with mutable
  * __proto__, which can change the type of an object dynamically.
  */
@@ -644,49 +646,56 @@ MarkTypeObjectUnknownProperties(JSContex
     }
 }
 
 /*
  * Mark any property which has been deleted or configured to be non-writable or
  * have a getter/setter.
  */
 inline void
-MarkTypePropertyConfigured(JSContext *cx, HandleObject obj, jsid id)
+MarkTypePropertyConfigured(ExclusiveContext *cx, HandleObject obj, jsid id)
 {
-    if (cx->typeInferenceEnabled())
+    if (cx->typeInferenceEnabled()) {
         id = IdToTypeId(id);
-    if (TrackPropertyTypes(cx, obj, id))
-        obj->type()->markPropertyConfigured(cx, id);
+        if (TrackPropertyTypes(cx, obj, id))
+            obj->type()->markPropertyConfigured(cx->asJSContext(), id);
+    }
 }
 
 /* Mark a state change on a particular object. */
 inline void
-MarkObjectStateChange(JSContext *cx, JSObject *obj)
+MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj)
 {
     if (cx->typeInferenceEnabled() && !obj->hasLazyType() && !obj->type()->unknownProperties())
-        obj->type()->markStateChange(cx);
+        obj->type()->markStateChange(cx->asJSContext());
 }
 
 /*
  * For an array or object which has not yet escaped and been referenced elsewhere,
  * pick a new type based on the object's current contents.
  */
 
 inline void
-FixArrayType(JSContext *cx, HandleObject obj)
+FixArrayType(ExclusiveContext *cxArg, HandleObject obj)
 {
-    if (cx->typeInferenceEnabled())
-        cx->compartment()->types.fixArrayType(cx, obj);
+    if (cxArg->isJSContext()) {
+        JSContext *cx = cxArg->asJSContext();
+        if (cx->typeInferenceEnabled())
+            cx->compartment()->types.fixArrayType(cx, obj);
+    }
 }
 
 inline void
-FixObjectType(JSContext *cx, HandleObject obj)
+FixObjectType(ExclusiveContext *cxArg, HandleObject obj)
 {
-    if (cx->typeInferenceEnabled())
-        cx->compartment()->types.fixObjectType(cx, obj);
+    if (cxArg->isJSContext()) {
+        JSContext *cx = cxArg->asJSContext();
+        if (cx->typeInferenceEnabled())
+            cx->compartment()->types.fixObjectType(cx, obj);
+    }
 }
 
 /* Interface helpers for JSScript*. */
 extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               const js::Value &rval);
 extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc,
                               js::types::Type type);
 
@@ -780,17 +789,17 @@ TypeScript::BytecodeTypes(JSScript *scri
 }
 
 /* static */ inline TypeObject *
 TypeScript::StandardType(JSContext *cx, JSProtoKey key)
 {
     RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto, NULL))
         return NULL;
-    return proto->getNewType(cx, GetClassForProtoKey(key));
+    return cx->getNewType(GetClassForProtoKey(key), proto.get());
 }
 
 struct AllocationSiteKey {
     JSScript *script;
 
     uint32_t offset : 24;
     JSProtoKey kind : 8;
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -337,17 +337,17 @@ GetCustomIterator(JSContext *cx, HandleO
     if (!Invoke(cx, ObjectValue(*obj), vp, 1, &arg, vp))
         return false;
     if (vp.isPrimitive()) {
         /*
          * We are always coming from js::ValueToIterator, and we are no longer on
          * trace, so the object we are iterating over is on top of the stack (-1).
          */
         JSAutoByteString bytes;
-        if (!js_AtomToPrintableString(cx, name, &bytes))
+        if (!AtomToPrintableString(cx, name, &bytes))
             return false;
         RootedValue val(cx, ObjectValue(*obj));
         js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
                              -1, val, NullPtr(), bytes.ptr());
         return false;
     }
     return true;
 }
@@ -370,17 +370,17 @@ Compare(T *a, T *b, size_t c)
     }
     return true;
 }
 
 static inline PropertyIteratorObject *
 NewPropertyIteratorObject(JSContext *cx, unsigned flags)
 {
     if (flags & JSITER_ENUMERATE) {
-        RootedTypeObject type(cx, cx->compartment()->getNewType(cx, &PropertyIteratorObject::class_, NULL));
+        RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, NULL));
         if (!type)
             return NULL;
 
         JSObject *metadata = NULL;
         if (!NewObjectMetadata(cx, &metadata))
             return NULL;
 
         Class *clasp = &PropertyIteratorObject::class_;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -48,35 +48,36 @@ using mozilla::PodCopy;
 using mozilla::RangedPtr;
 
 /*
  * If we're accumulating a decimal number and the number is >= 2^53, then the
  * fast result from the loop in GetPrefixInteger may be inaccurate. Call
  * js_strtod_harder to get the correct answer.
  */
 static bool
-ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar *end, double *dp)
+ComputeAccurateDecimalInteger(ExclusiveContext *cx,
+                              const jschar *start, const jschar *end, double *dp)
 {
     size_t length = end - start;
     char *cstr = cx->pod_malloc<char>(length + 1);
     if (!cstr)
         return false;
 
     for (size_t i = 0; i < length; i++) {
         char c = char(start[i]);
         JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
         cstr[i] = c;
     }
     cstr[length] = 0;
 
     char *estr;
     int err = 0;
-    *dp = js_strtod_harder(cx->mainThread().dtoaState, cstr, &estr, &err);
+    *dp = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
     if (err == JS_DTOA_ENOMEM) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportOutOfMemory(cx);
         js_free(cstr);
         return false;
     }
     if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL)
         *dp = js_PositiveInfinity;
     js_free(cstr);
     return true;
 }
@@ -181,17 +182,17 @@ js::ParseDecimalNumber(const JS::TwoByte
         MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
                    "next value won't be an integrally-precise double");
         dec = next;
     } while (++s < end);
     return static_cast<double>(dec);
 }
 
 bool
-js::GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base,
+js::GetPrefixInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, int base,
                      const jschar **endp, double *dp)
 {
     JS_ASSERT(start <= end);
     JS_ASSERT(2 <= base && base <= 36);
 
     const jschar *s = start;
     double d = 0.0;
     for (; s < end; s++) {
@@ -505,56 +506,50 @@ ToCStringBuf::ToCStringBuf() :dbuf(NULL)
 ToCStringBuf::~ToCStringBuf()
 {
     if (dbuf)
         js_free(dbuf);
 }
 
 template <AllowGC allowGC>
 JSFlatString *
-js::Int32ToString(ThreadSafeContext *tcx, int32_t si)
+js::Int32ToString(ThreadSafeContext *cx, int32_t si)
 {
     uint32_t ui;
     if (si >= 0) {
         if (StaticStrings::hasInt(si))
-            return tcx->staticStrings().getInt(si);
+            return cx->staticStrings().getInt(si);
         ui = si;
     } else {
         ui = uint32_t(-si);
         JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
     }
 
-    JSCompartment *c = NULL;
-    if (tcx->isJSContext()) {
-        c = tcx->asJSContext()->compartment();
-        if (JSFlatString *str = c->dtoaCache.lookup(10, si))
+    if (cx->isExclusiveContext()) {
+        if (JSFlatString *str = cx->asExclusiveContext()->dtoaCache().lookup(10, si))
             return str;
     }
 
-    JSShortString *str = js_NewGCShortString<allowGC>(tcx);
+    JSShortString *str = js_NewGCShortString<allowGC>(cx);
     if (!str)
         return NULL;
 
     jschar buffer[JSShortString::MAX_SHORT_LENGTH + 1];
     RangedPtr<jschar> end(buffer + JSShortString::MAX_SHORT_LENGTH,
                           buffer, JSShortString::MAX_SHORT_LENGTH + 1);
     *end = '\0';
     RangedPtr<jschar> start = BackfillIndexInCharBuffer(ui, end);
     if (si < 0)
         *--start = '-';
 
     jschar *dst = str->init(end - start);
     PodCopy(dst, start.get(), end - start + 1);
 
-    /*
-     * Only attempt to cache the result if we have a JSContext, as it is
-     * racy.
-     */
-    if (c)
-        c->dtoaCache.cache(10, si, str);
+    if (cx->isExclusiveContext())
+        cx->asExclusiveContext()->dtoaCache().cache(10, si, str);
     return str;
 }
 
 template JSFlatString *
 js::Int32ToString<CanGC>(ThreadSafeContext *cx, int32_t si);
 
 template JSFlatString *
 js::Int32ToString<NoGC>(ThreadSafeContext *cx, int32_t si);
@@ -1189,17 +1184,17 @@ js_InitNumberClass(JSContext *cx, Handle
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Number, ctor, numberProto))
         return NULL;
 
     return numberProto;
 }
 
 static char *
-FracNumberToCString(ThreadSafeContext *tcx, ToCStringBuf *cbuf, double d, int base = 10)
+FracNumberToCString(ThreadSafeContext *cx, ToCStringBuf *cbuf, double d, int base = 10)
 {
 #ifdef DEBUG
     {
         int32_t _;
         JS_ASSERT(!mozilla::DoubleIsInt32(d, &_));
     }
 #endif
 
@@ -1213,108 +1208,101 @@ FracNumberToCString(ThreadSafeContext *t
          *   Florian Loitsch, PLDI 2010.
          */
         const double_conversion::DoubleToStringConverter &converter
             = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
         double_conversion::StringBuilder builder(cbuf->sbuf, cbuf->sbufSize);
         converter.ToShortest(d, &builder);
         numStr = builder.Finalize();
     } else {
-        numStr = cbuf->dbuf = js_dtobasestr(tcx->perThreadData->dtoaState, base, d);
+        numStr = cbuf->dbuf = js_dtobasestr(cx->dtoaState(), base, d);
     }
     return numStr;
 }
 
 char *
 js::NumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base/* = 10*/)
 {
     int32_t i;
     return mozilla::DoubleIsInt32(d, &i)
            ? IntToCString(cbuf, i, base)
            : FracNumberToCString(cx, cbuf, d, base);
 }
 
 template <AllowGC allowGC>
 static JSString * JS_FASTCALL
-js_NumberToStringWithBase(ThreadSafeContext *tcx, double d, int base)
+js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base)
 {
     ToCStringBuf cbuf;
     char *numStr;
 
     /*
      * Caller is responsible for error reporting. When called from trace,
      * returning NULL here will cause us to fall of trace and then retry
      * from the interpreter (which will report the error).
      */
     if (base < 2 || base > 36)
         return NULL;
 
-    JSCompartment *c = NULL;
-    JSContext *cx = NULL;
-    if (tcx->isJSContext()) {
-        cx = tcx->asJSContext();
-        c = cx->compartment();
-    }
-
     int32_t i;
     if (mozilla::DoubleIsInt32(d, &i)) {
         if (base == 10 && StaticStrings::hasInt(i))
-            return tcx->staticStrings().getInt(i);
+            return cx->staticStrings().getInt(i);
         if (unsigned(i) < unsigned(base)) {
             if (i < 10)
-                return tcx->staticStrings().getInt(i);
+                return cx->staticStrings().getInt(i);
             jschar c = 'a' + i - 10;
             JS_ASSERT(StaticStrings::hasUnit(c));
-            return tcx->staticStrings().getUnit(c);
+            return cx->staticStrings().getUnit(c);
         }
 
-        if (JSFlatString *str = c ? c->dtoaCache.lookup(base, d) : NULL)
-            return str;
+        if (cx->isExclusiveContext()) {
+            if (JSFlatString *str = cx->asExclusiveContext()->dtoaCache().lookup(base, d))
+                return str;
+        }
 
         numStr = IntToCString(&cbuf, i, base);
         JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
     } else {
-        if (JSFlatString *str = c ? c->dtoaCache.lookup(base, d) : NULL)
-            return str;
+        if (cx->isExclusiveContext()) {
+            if (JSFlatString *str = cx->asExclusiveContext()->dtoaCache().lookup(base, d))
+                return str;
+        }
 
-        numStr = FracNumberToCString(tcx, &cbuf, d, base);
+        numStr = FracNumberToCString(cx, &cbuf, d, base);
         if (!numStr) {
-            if (cx)
-                JS_ReportOutOfMemory(cx);
+            js_ReportOutOfMemory(cx);
             return NULL;
         }
         JS_ASSERT_IF(base == 10,
                      !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
         JS_ASSERT_IF(base != 10,
                      cbuf.dbuf && cbuf.dbuf == numStr);
     }
 
-    JSFlatString *s = js_NewStringCopyZ<allowGC>(tcx, numStr);
+    JSFlatString *s = js_NewStringCopyZ<allowGC>(cx, numStr);
 
-    /*
-     * We will only cache dtoa results if we have a JSContext, as it is
-     * racy.
-     */
-    if (c)
-        c->dtoaCache.cache(base, d, s);
+    if (cx->isExclusiveContext())
+        cx->asExclusiveContext()->dtoaCache().cache(base, d, s);
+
     return s;
 }
 
 template <AllowGC allowGC>
 JSString *
-js_NumberToString(ThreadSafeContext *tcx, double d)
+js_NumberToString(ThreadSafeContext *cx, double d)
 {
-    return js_NumberToStringWithBase<allowGC>(tcx, d, 10);
+    return js_NumberToStringWithBase<allowGC>(cx, d, 10);
 }
 
 template JSString *
-js_NumberToString<CanGC>(ThreadSafeContext *tcx, double d);
+js_NumberToString<CanGC>(ThreadSafeContext *cx, double d);
 
 template JSString *
-js_NumberToString<NoGC>(ThreadSafeContext *tcx, double d);
+js_NumberToString<NoGC>(ThreadSafeContext *cx, double d);
 
 JSFlatString *
 js::NumberToString(JSContext *cx, double d)
 {
     if (JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10))
         return &str->asFlat();
     return NULL;
 }
@@ -1367,17 +1355,17 @@ js::NumberValueToStringBuffer(JSContext 
      * even if jschars are UTF-8, all chars should map to one jschar.
      */
     size_t cstrlen = strlen(cstr);
     JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
     return sb.appendInflated(cstr, cstrlen);
 }
 
 bool
-js::StringToNumber(JSContext *cx, JSString *str, double *result)
+js::StringToNumber(ExclusiveContext *cx, JSString *str, double *result)
 {
     size_t length = str->length();
     const jschar *chars = str->getChars(NULL);
     if (!chars)
         return false;
 
     if (length == 1) {
         jschar c = chars[0];
@@ -1431,18 +1419,19 @@ js::StringToNumber(JSContext *cx, JSStri
     }
     *result = d;
     return true;
 }
 
 #if defined(_MSC_VER)
 # pragma optimize("g", off)
 #endif
-JS_PUBLIC_API(bool)
-js::ToNumberSlow(JSContext *cx, Value v, double *out)
+
+bool
+js::ToNumberSlow(ExclusiveContext *cx, Value v, double *out)
 {
 #ifdef DEBUG
     /*
      * MSVC bizarrely miscompiles this, complaining about the first brace below
      * being unmatched (!).  The error message points at both this opening brace
      * and at the corresponding SkipRoot constructor.  The error seems to derive
      * from the presence guard-object macros on the SkipRoot class/constructor,
      * which seems well in the weeds for an unmatched-brace syntax error.
@@ -1480,26 +1469,33 @@ js::ToNumberSlow(JSContext *cx, Value v,
             *out = 0.0;
             return true;
         }
         if (v.isUndefined())
             break;
 
         JS_ASSERT(v.isObject());
         RootedValue v2(cx, v);
-        if (!ToPrimitive(cx, JSTYPE_NUMBER, &v2))
+        if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
             return false;
         v = v2;
         if (v.isObject())
             break;
     }
 
     *out = js_NaN;
     return true;
 }
+
+JS_PUBLIC_API(bool)
+js::ToNumberSlow(JSContext *cx, Value v, double *out)
+{
+    return ToNumberSlow(static_cast<ExclusiveContext *>(cx), v, out);
+}
+
 #if defined(_MSC_VER)
 # pragma optimize("", on)
 #endif
 
 /*
  * Convert a value to an int64_t, according to the WebIDL rules for long long
  * conversion. Return converted value in *out on success, false on failure.
  */
@@ -1596,17 +1592,17 @@ js::ToUint16Slow(JSContext *cx, const Va
     d = fmod(d, (double) m);
     if (d < 0)
         d += m;
     *out = (uint16_t) d;
     return true;
 }
 
 JSBool
-js_strtod(JSContext *cx, const jschar *s, const jschar *send,
+js_strtod(ExclusiveContext *cx, const jschar *s, const jschar *send,
           const jschar **ep, double *dp)
 {
     size_t i;
     char cbuf[32];
     char *cstr, *istr, *estr;
     JSBool negative;
     double d;
 
@@ -1632,17 +1628,17 @@ js_strtod(JSContext *cx, const jschar *s
     istr = cstr;
     if ((negative = (*istr == '-')) != 0 || *istr == '+')
         istr++;
     if (*istr == 'I' && !strncmp(istr, "Infinity", 8)) {
         d = negative ? js_NegativeInfinity : js_PositiveInfinity;
         estr = istr + 8;
     } else {
         int err;
-        d = js_strtod_harder(cx->mainThread().dtoaState, cstr, &estr, &err);
+        d = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
         if (d == HUGE_VAL)
             d = js_PositiveInfinity;
         else if (d == -HUGE_VAL)
             d = js_NegativeInfinity;
     }
 
     i = estr - cstr;
     if (cstr != cbuf)
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -50,17 +50,17 @@ class JSString;
 template <js::AllowGC allowGC>
 extern JSString *
 js_NumberToString(js::ThreadSafeContext *cx, double d);
 
 namespace js {
 
 template <AllowGC allowGC>
 extern JSFlatString *
-Int32ToString(ThreadSafeContext *tcx, int32_t i);
+Int32ToString(ThreadSafeContext *cx, int32_t i);
 
 /*
  * Convert an integer or double (contained in the given value) to a string and
  * append to the given buffer.
  */
 extern bool JS_FASTCALL
 NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb);
 
@@ -123,21 +123,21 @@ ParseDecimalNumber(const JS::TwoByteChar
  * digits of the integer in *endp, and return the integer itself in *dp.  If
  * base is 10 or a power of two the returned integer is the closest possible
  * double; otherwise extremely large integers may be slightly inaccurate.
  *
  * If [start, end) does not begin with a number with the specified base,
  * *dp == 0 and *endp == start upon return.
  */
 extern bool
-GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base,
+GetPrefixInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, int base,
                  const jschar **endp, double *dp);
 
 extern bool
-StringToNumber(JSContext *cx, JSString *str, double *result);
+StringToNumber(ExclusiveContext *cx, JSString *str, double *result);
 
 /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
 JS_ALWAYS_INLINE bool
 ToNumber(JSContext *cx, JS::MutableHandleValue vp)
 {
 #ifdef DEBUG
     MaybeCheckStackRoots(cx);
 #endif
@@ -164,17 +164,17 @@ num_parseInt(JSContext *cx, unsigned arg
  * return the closest double number to the given input in dp.
  *
  * Also allows inputs of the form [+|-]Infinity, which produce an infinity of
  * the appropriate sign.  The case of the "Infinity" string must match exactly.
  * If the string does not contain a number, set *ep to s and return 0.0 in dp.
  * Return false if out of memory.
  */
 extern JSBool
-js_strtod(JSContext *cx, const jschar *s, const jschar *send,
+js_strtod(js::ExclusiveContext *cx, const jschar *s, const jschar *send,
           const jschar **ep, double *dp);
 
 extern JSBool
 js_num_valueOf(JSContext *cx, unsigned argc, js::Value *vp);
 
 namespace js {
 
 static JS_ALWAYS_INLINE bool
@@ -258,11 +258,26 @@ SafeSub(int32_t one, int32_t two, int32_
 inline bool
 SafeMul(int32_t one, int32_t two, int32_t *res)
 {
     *res = one * two;
     int64_t ores = (int64_t)one * (int64_t)two;
     return ores == (int64_t)*res;
 }
 
+extern bool
+ToNumberSlow(ExclusiveContext *cx, Value v, double *dp);
+
+// Variant of ToNumber which takes an ExclusiveContext instead of a JSContext.
+// ToNumber is part of the API and can't use ExclusiveContext directly.
+JS_ALWAYS_INLINE bool
+ToNumber(ExclusiveContext *cx, const Value &v, double *out)
+{
+    if (v.isNumber()) {
+        *out = v.toNumber();
+        return true;
+    }
+    return ToNumberSlow(cx, v, out);
+}
+
 } /* namespace js */
 
 #endif /* jsnum_h */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1286,23 +1286,23 @@ NewObjectGCKind(js::Class *clasp)
     if (clasp == &ArrayObject::class_)
         return gc::FINALIZE_OBJECT8;
     if (clasp == &JSFunction::class_)
         return gc::FINALIZE_OBJECT2;
     return gc::FINALIZE_OBJECT4;
 }
 
 static inline JSObject *
-NewObject(JSContext *cx, Class *clasp, types::TypeObject *type_, JSObject *parent,
+NewObject(ExclusiveContext *cx, Class *clasp, types::TypeObject *type_, JSObject *parent,
           gc::AllocKind kind, NewObjectKind newKind)
 {
     JS_ASSERT(clasp != &ArrayObject::class_);
     JS_ASSERT_IF(clasp == &JSFunction::class_,
                  kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
-    JS_ASSERT_IF(parent, &parent->global() == cx->compartment()->maybeGlobal());
+    JS_ASSERT_IF(parent, &parent->global() == cx->global());
 
     RootedTypeObject type(cx, type_);
 
     JSObject *metadata = NULL;
     if (!NewObjectMetadata(cx, &metadata))
         return NULL;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(type->proto),
@@ -1322,134 +1322,142 @@ NewObject(JSContext *cx, Class *clasp, t
         obj = nobj;
     }
 
     /*
      * This will cancel an already-running incremental GC from doing any more
      * slices, and it will prevent any future incremental GCs.
      */
     if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
-        cx->runtime()->gcIncrementalEnabled = false;
+        cx->asJSContext()->runtime()->gcIncrementalEnabled = false;
 
     Probes::createObject(cx, obj);
     return obj;
 }
 
 void
 NewObjectCache::fillProto(EntryIndex entry, Class *clasp, js::TaggedProto proto,
                           gc::AllocKind kind, JSObject *obj)
 {
     JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
     JS_ASSERT(obj->getTaggedProto() == proto);
     return fill(entry, clasp, proto.raw(), kind, obj);
 }
 
 JSObject *
-js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp,
+js::NewObjectWithGivenProto(ExclusiveContext *cxArg, js::Class *clasp,
                             js::TaggedProto proto_, JSObject *parent_,
                             gc::AllocKind allocKind, NewObjectKind newKind)
 {
-    Rooted<TaggedProto> proto(cx, proto_);
-    RootedObject parent(cx, parent_);
+    Rooted<TaggedProto> proto(cxArg, proto_);
+    RootedObject parent(cxArg, parent_);
 
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
-    NewObjectCache &cache = cx->runtime()->newObjectCache;
-
     NewObjectCache::EntryIndex entry = -1;
-    if (proto.isObject() &&
-        newKind == GenericObject &&
-        !cx->compartment()->objectMetadataCallback &&
-        (!parent || parent == proto.toObject()->getParent()) &&
-        !proto.toObject()->is<GlobalObject>())
-    {
-        if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
-            JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
-            if (obj)
-                return obj;
+    if (JSContext *cx = cxArg->maybeJSContext()) {
+        NewObjectCache &cache = cx->runtime()->newObjectCache;
+        if (proto.isObject() &&
+            newKind == GenericObject &&
+            !cx->compartment()->objectMetadataCallback &&
+            (!parent || parent == proto.toObject()->getParent()) &&
+            !proto.toObject()->is<GlobalObject>())
+        {
+            if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
+                JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
+                if (obj)
+                    return obj;
+            }
         }
     }
 
-    types::TypeObject *type = cx->compartment()->getNewType(cx, clasp, proto, NULL);
+    types::TypeObject *type = cxArg->getNewType(clasp, proto, NULL);
     if (!type)
         return NULL;
 
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     if (!parent && proto.isObject())
         parent = proto.toObject()->getParent();
 
-    RootedObject obj(cx, NewObject(cx, clasp, type, parent, allocKind, newKind));
+    RootedObject obj(cxArg, NewObject(cxArg, clasp, type, parent, allocKind, newKind));
     if (!obj)
         return NULL;
 
-    if (entry != -1 && !obj->hasDynamicSlots())
-        cache.fillProto(entry, clasp, proto, allocKind, obj);
+    if (entry != -1 && !obj->hasDynamicSlots()) {
+        cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
+                                                                  proto, allocKind, obj);
+    }
 
     return obj;
 }
 
 JSObject *
-js::NewObjectWithClassProtoCommon(JSContext *cx, js::Class *clasp, JSObject *protoArg, JSObject *parentArg,
+js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg,
+                                  js::Class *clasp, JSObject *protoArg, JSObject *parentArg,
                                   gc::AllocKind allocKind, NewObjectKind newKind)
 {
     if (protoArg)
-        return NewObjectWithGivenProto(cx, clasp, protoArg, parentArg, allocKind, newKind);
+        return NewObjectWithGivenProto(cxArg, clasp, protoArg, parentArg, allocKind, newKind);
 
     if (CanBeFinalizedInBackground(allocKind, clasp))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     if (!parentArg)
-        parentArg = cx->global();
+        parentArg = cxArg->global();
 
     /*
      * Use the object cache, except for classes without a cached proto key.
      * On these objects, FindProto will do a dynamic property lookup to get
      * global[className].prototype, where changes to either the className or
      * prototype property would render the cached lookup incorrect. For classes
      * with a proto key, the prototype created during class initialization is
      * stored in an immutable slot on the global (except for ClearScope, which
      * will flush the new object cache).
      */
     JSProtoKey protoKey = GetClassProtoKey(clasp);
 
-    NewObjectCache &cache = cx->runtime()->newObjectCache;
-
     NewObjectCache::EntryIndex entry = -1;
-    if (parentArg->is<GlobalObject>() &&
-        protoKey != JSProto_Null &&
-        newKind == GenericObject &&
-        !cx->compartment()->objectMetadataCallback)
-    {
-        if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
-            JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
-            if (obj)
-                return obj;
+    if (JSContext *cx = cxArg->maybeJSContext()) {
+        NewObjectCache &cache = cx->runtime()->newObjectCache;
+        if (parentArg->is<GlobalObject>() &&
+            protoKey != JSProto_Null &&
+            newKind == GenericObject &&
+            !cx->compartment()->objectMetadataCallback)
+        {
+            if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
+                JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
+                if (obj)
+                    return obj;
+            }
         }
     }
 
-    RootedObject parent(cx, parentArg);
-    RootedObject proto(cx, protoArg);
-
-    if (!FindProto(cx, clasp, &proto))
+    RootedObject parent(cxArg, parentArg);
+    RootedObject proto(cxArg, protoArg);
+
+    if (!FindProto(cxArg, clasp, &proto))
         return NULL;
 
-    types::TypeObject *type = proto->getNewType(cx, clasp);
+    types::TypeObject *type = cxArg->getNewType(clasp, proto.get());
     if (!type)
         return NULL;
 
-    JSObject *obj = NewObject(cx, clasp, type, parent, allocKind, newKind);
+    JSObject *obj = NewObject(cxArg, clasp, type, parent, allocKind, newKind);
     if (!obj)
         return NULL;
 
-    if (entry != -1 && !obj->hasDynamicSlots())
-        cache.fillGlobal(entry, clasp, &parent->as<GlobalObject>(), allocKind, obj);
+    if (entry != -1 && !obj->hasDynamicSlots()) {
+        cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp,
+                                                                   &parent->as<GlobalObject>(),
+                                                                   allocKind, obj);
+    }
 
     return obj;
 }
 
 /*
  * Create a plain object with the specified type. This bypasses getNewType to
  * avoid losing creation site information for objects made by scripted 'new'.
  */
@@ -1592,17 +1600,17 @@ CreateThisForFunctionWithType(JSContext 
 
 JSObject *
 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
                                   NewObjectKind newKind /* = GenericObject */)
 {
     RootedObject res(cx);
 
     if (proto) {
-        RootedTypeObject type(cx, proto->getNewType(cx, &ObjectClass, &callee->as<JSFunction>()));
+        RootedTypeObject type(cx, cx->getNewType(&ObjectClass, proto, &callee->as<JSFunction>()));
         if (!type)
             return NULL;
         res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
     } else {
         gc::AllocKind allocKind = NewObjectGCKind(&ObjectClass);
         res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), allocKind, newKind);
     }
 
@@ -1859,17 +1867,17 @@ js::CloneObject(JSContext *cx, HandleObj
 
     return clone;
 }
 
 JSObject *
 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
 {
     Rooted<TypeObject*> typeObj(cx);
-    typeObj = cx->global()->getOrCreateObjectPrototype(cx)->getNewType(cx, &ObjectClass);
+    typeObj = cx->getNewType(&ObjectClass, cx->global()->getOrCreateObjectPrototype(cx));
 
     JS_ASSERT(srcObj->getClass() == &ObjectClass);
     AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
     JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
 
     RootedShape shape(cx, srcObj->lastProperty());
     return NewReshapedObject(cx, typeObj, parent, kind, shape);
 }
@@ -2447,17 +2455,18 @@ js_InitClass(JSContext *cx, HandleObject
         return NULL;
     }
 
     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
                                          ps, fs, static_ps, static_fs, ctorp, ctorKind);
 }
 
 /* static */ inline bool
-JSObject::updateSlotsForSpan(JSContext *cx, HandleObject obj, size_t oldSpan, size_t newSpan)
+JSObject::updateSlotsForSpan(ExclusiveContext *cx,
+                             HandleObject obj, size_t oldSpan, size_t newSpan)
 {
     JS_ASSERT(oldSpan != newSpan);
 
     size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan);
     size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan);
 
     if (oldSpan < newSpan) {
         if (oldCount < newCount && !JSObject::growSlots(cx, obj, oldCount, newCount))
@@ -2475,17 +2484,17 @@ JSObject::updateSlotsForSpan(JSContext *
         if (oldCount > newCount)
             JSObject::shrinkSlots(cx, obj, oldCount, newCount);
     }
 
     return true;
 }
 
 /* static */ bool
-JSObject::setLastProperty(JSContext *cx, HandleObject obj, HandleShape shape)
+JSObject::setLastProperty(ExclusiveContext *cx, HandleObject obj, HandleShape shape)
 {
     JS_ASSERT(!obj->inDictionaryMode());
     JS_ASSERT(!shape->inDictionary());
     JS_ASSERT(shape->compartment() == obj->compartment());
     JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
 
     size_t oldSpan = obj->lastProperty()->slotSpan();
     size_t newSpan = shape->slotSpan();
@@ -2498,55 +2507,58 @@ JSObject::setLastProperty(JSContext *cx,
     if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
         return false;
 
     obj->shape_ = shape;
     return true;
 }
 
 /* static */ bool
-JSObject::setSlotSpan(JSContext *cx, HandleObject obj, uint32_t span)
+JSObject::setSlotSpan(ExclusiveContext *cx, HandleObject obj, uint32_t span)
 {
     JS_ASSERT(obj->inDictionaryMode());
 
     size_t oldSpan = obj->lastProperty()->base()->slotSpan();
     if (oldSpan == span)
         return true;
 
     if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span))
         return false;
 
     obj->lastProperty()->base()->setSlotSpan(span);
     return true;
 }
 
 static HeapSlot *
-AllocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots)
+AllocateSlots(ExclusiveContext *cx, JSObject *obj, uint32_t nslots)
 {
 #ifdef JSGC_GENERATIONAL
-    return cx->runtime()->gcNursery.allocateSlots(cx, obj, nslots);
-#else
+    if (cx->isJSContext())
+        return cx->asJSContext()->runtime()->gcNursery.allocateSlots(cx->asJSContext(), obj, nslots);
+#endif
     return cx->pod_malloc<HeapSlot>(nslots);
-#endif
 }
 
 static HeapSlot *
-ReallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots,
+ReallocateSlots(ExclusiveContext *cx, JSObject *obj, HeapSlot *oldSlots,
                 uint32_t oldCount, uint32_t newCount)
 {
 #ifdef JSGC_GENERATIONAL
-    return cx->runtime()->gcNursery.reallocateSlots(cx, obj, oldSlots, oldCount, newCount);
-#else
+    if (cx->isJSContext()) {
+        return cx->asJSContext()->runtime()->gcNursery.reallocateSlots(cx->asJSContext(),
+                                                                       obj, oldSlots,
+                                                                       oldCount, newCount);
+    }
+#endif
     return (HeapSlot *)cx->realloc_(oldSlots, oldCount * sizeof(HeapSlot),
                                     newCount * sizeof(HeapSlot));
-#endif
 }
 
 /* static */ bool
-JSObject::growSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
+JSObject::growSlots(ExclusiveContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
 {
     JS_ASSERT(newCount > oldCount);
     JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
@@ -2557,28 +2569,32 @@ JSObject::growSlots(JSContext *cx, Handl
      * If we are allocating slots for an object whose type is always created
      * by calling 'new' on a particular script, bump the GC kind for that
      * type to give these objects a larger number of fixed slots when future
      * objects are constructed.
      */
     if (!obj->hasLazyType() && !oldCount && obj->type()->newScript) {
         gc::AllocKind kind = obj->type()->newScript->allocKind;
         uint32_t newScriptSlots = gc::GetGCKindSlots(kind);
-        if (newScriptSlots == obj->numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
-            AutoEnterAnalysis enter(cx);
+        if (newScriptSlots == obj->numFixedSlots() &&
+            gc::TryIncrementAllocKind(&kind) &&
+            cx->isJSContext())
+        {
+            JSContext *ncx = cx->asJSContext();
+            AutoEnterAnalysis enter(ncx);
 
             Rooted<TypeObject*> typeObj(cx, obj->type());
             RootedShape shape(cx, typeObj->newScript->shape);
-            JSObject *reshapedObj = NewReshapedObject(cx, typeObj, obj->getParent(), kind, shape);
+            JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape);
             if (!reshapedObj)
                 return false;
 
             typeObj->newScript->allocKind = kind;
             typeObj->newScript->shape = reshapedObj->lastProperty();
-            typeObj->markStateChange(cx);
+            typeObj->markStateChange(ncx);
         }
     }
 
     if (!oldCount) {
         obj->slots = AllocateSlots(cx, obj, newCount);
         if (!obj->slots)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
@@ -2597,26 +2613,26 @@ JSObject::growSlots(JSContext *cx, Handl
     /* Changes in the slots of global objects can trigger recompilation. */
     if (changed && obj->is<GlobalObject>())
         types::MarkObjectStateChange(cx, obj);
 
     return true;
 }
 
 static void
-FreeSlots(JSContext *cx, HeapSlot *slots)
+FreeSlots(ExclusiveContext *cx, HeapSlot *slots)
 {
 #ifdef JSGC_GENERATIONAL
-    if (!cx->runtime()->gcNursery.isInside(slots))
+    if (!cx->isJSContext() || !cx->asJSContext()->runtime()->gcNursery.isInside(slots))
 #endif
         js_free(slots);
 }
 
 /* static */ void
-JSObject::shrinkSlots(JSContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
+JSObject::shrinkSlots(ExclusiveContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
 {
     JS_ASSERT(newCount < oldCount);
 
     if (newCount == 0) {
         FreeSlots(cx, obj->slots);
         obj->slots = NULL;
         return;
     }
@@ -2631,17 +2647,17 @@ JSObject::shrinkSlots(JSContext *cx, Han
     obj->slots = newslots;
 
     /* Watch for changes in global object slots, as for growSlots. */
     if (changed && obj->is<GlobalObject>())
         types::MarkObjectStateChange(cx, obj);
 }
 
 /* static */ bool
-JSObject::sparsifyDenseElement(JSContext *cx, HandleObject obj, uint32_t index)
+JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index)
 {
     RootedValue value(cx, obj->getDenseElement(index));
     JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
 
     JSObject::removeDenseElementForSparseIndex(cx, obj, index);
 
     uint32_t slot = obj->slotSpan();
     if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
@@ -2651,17 +2667,17 @@ JSObject::sparsifyDenseElement(JSContext
 
     JS_ASSERT(slot == obj->slotSpan() - 1);
     obj->initSlot(slot, value);
 
     return true;
 }
 
 /* static */ bool
-JSObject::sparsifyDenseElements(JSContext *cx, HandleObject obj)
+JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj)
 {
     uint32_t initialized = obj->getDenseInitializedLength();
 
     /* Create new properties with the value of non-hole dense elements. */
     for (uint32_t i = 0; i < initialized; i++) {
         if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
             continue;
 
@@ -2710,17 +2726,17 @@ JSObject::willBeSparseElements(uint32_t 
     for (uint32_t i = 0; i < len; i++) {
         if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
             return false;
     }
     return true;
 }
 
 /* static */ JSObject::EnsureDenseResult
-JSObject::maybeDensifySparseElements(JSContext *cx, HandleObject obj)
+JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj)
 {
     /*
      * Wait until after the object goes into dictionary mode, which must happen
      * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
      * (see PropertyTree::MAX_HEIGHT).
      */
     if (!obj->inDictionaryMode())
         return ED_SPARSE;
@@ -2916,17 +2932,17 @@ JSObject::growElements(ThreadSafeContext
     elements = newheader->elements();
 
     Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
 
     return true;
 }
 
 void
-JSObject::shrinkElements(JSContext *cx, uint32_t newcap)
+JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t newcap)
 {
     uint32_t oldcap = getDenseCapacity();
     JS_ASSERT(newcap <= oldcap);
 
     /* Don't shrink elements below the minimum capacity. */
     if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
         return;
 
@@ -3026,17 +3042,17 @@ js::SetClassAndProto(JSContext *cx, Hand
     }
 
     if (proto.isObject()) {
         RootedObject protoObj(cx, proto.toObject());
         if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj))
             return false;
     }
 
-    TypeObject *type = cx->compartment()->getNewType(cx, clasp, proto);
+    TypeObject *type = cx->getNewType(clasp, proto);
     if (!type)
         return false;
 
     /*
      * Setting __proto__ on an object that has escaped and may be referenced by
      * other heap objects can only be done if the properties of both objects
      * are unknown. Type sets containing this object will contain the original
      * type but not the new type of the object, so we need to go and scan the
@@ -3046,30 +3062,33 @@ js::SetClassAndProto(JSContext *cx, Hand
     MarkTypeObjectUnknownProperties(cx, obj->type(), true);
     MarkTypeObjectUnknownProperties(cx, type, true);
 
     obj->setType(type);
     return true;
 }
 
 bool
-js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, MutableHandleObject objp)
+js_GetClassObject(ExclusiveContext *cxArg, JSObject *obj, JSProtoKey key, MutableHandleObject objp)
 {
-    RootedObject global(cx, &obj->global());
+    RootedObject global(cxArg, &obj->global());
     if (!global->is<GlobalObject>()) {
         objp.set(NULL);
         return true;
     }
 
     Value v = global->getReservedSlot(key);
     if (v.isObject()) {
         objp.set(&v.toObject());
         return true;
     }
 
+    // Classes can only be initialized on the main thread.
+    JSContext *cx = cxArg->asJSContext();
+
     RootedId name(cx, NameToId(ClassName(key, cx)));
     AutoResolving resolving(cx, global, name);
     if (resolving.alreadyStarted()) {
         /* Already caching id in global -- suppress recursion. */
         objp.set(NULL);
         return true;
     }
 
@@ -3105,17 +3124,17 @@ js_IdentifyClassPrototype(JSObject *obj)
     if (v.isObject() && obj == &v.toObject())
         return key;
 
     // False alarm - just an instance.
     return JSProto_Null;
 }
 
 bool
-js_FindClassObject(JSContext *cx, JSProtoKey protoKey, MutableHandleValue vp, Class *clasp)
+js_FindClassObject(ExclusiveContext *cx, JSProtoKey protoKey, MutableHandleValue vp, Class *clasp)
 {
     RootedId id(cx);
 
     if (protoKey != JSProto_Null) {
         JS_ASSERT(JSProto_Null < protoKey);
         JS_ASSERT(protoKey < JSProto_LIMIT);
         RootedObject cobj(cx);
         if (!js_GetClassObject(cx, cx->global(), protoKey, &cobj))
@@ -3144,17 +3163,17 @@ js_FindClassObject(JSContext *cx, JSProt
                 v.get().setUndefined();
         }
     }
     vp.set(v);
     return true;
 }
 
 /* static */ bool
-JSObject::allocSlot(JSContext *cx, HandleObject obj, uint32_t *slotp)
+JSObject::allocSlot(ExclusiveContext *cx, HandleObject obj, uint32_t *slotp)
 {
     uint32_t slot = obj->slotSpan();
     JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
 
     /*
      * If this object is in dictionary mode, try to pull a free slot from the
      * shape table's slot-number freelist.
      */
@@ -3211,17 +3230,17 @@ JSObject::freeSlot(uint32_t slot)
             last = slot;
             return;
         }
     }
     setSlot(slot, UndefinedValue());
 }
 
 static bool
-PurgeProtoChain(JSContext *cx, JSObject *objArg, HandleId id)
+PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
 {
     /* Root locally so we can re-assign. */
     RootedObject obj(cx, objArg);
 
     RootedShape shape(cx);
     while (obj) {
         /* Lookups will not be cached through non-native protos. */
         if (!obj->isNative())
@@ -3237,17 +3256,17 @@ PurgeProtoChain(JSContext *cx, JSObject 
         }
         obj = obj->getProto();
     }
 
     return true;
 }
 
 static bool
-PurgeScopeChainHelper(JSContext *cx, HandleObject objArg, HandleId id)
+PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
 {
     /* Re-root locally so we can re-assign. */
     RootedObject obj(cx, objArg);
 
     JS_ASSERT(obj->isNative());
     JS_ASSERT(obj->isDelegate());
 
     /* Lookups on integer ids cannot be cached through prototypes. */
@@ -3274,24 +3293,23 @@ PurgeScopeChainHelper(JSContext *cx, Han
 
 /*
  * PurgeScopeChain does nothing if obj is not itself a prototype or parent
  * scope, else it reshapes the scope and prototype chains it links. It calls
  * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
  * (i.e., obj has ever been on a prototype or parent chain).
  */
 static inline bool
-PurgeScopeChain(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
+PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
 {
     if (obj->isDelegate())
         return PurgeScopeChainHelper(cx, obj, id);
     return true;
 }
 
-
 Shape *
 js_AddNativeProperty(JSContext *cx, HandleObject obj, HandleId id,
                      PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
                      unsigned attrs, unsigned flags, int shortid)
 {
     /*
      * Purge the property cache of now-shadowed id in obj's scope chain. Do
      * this optimistically (assuming no failure below) before locking obj, so
@@ -3307,97 +3325,157 @@ js_AddNativeProperty(JSContext *cx, Hand
 
     if (JSID_IS_INT(id))
         JSObject::removeDenseElementForSparseIndex(cx, obj, JSID_TO_INT(id));
 
     return shape;
 }
 
 JSBool
-baseops::DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
+baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
+/* static */ JSBool
+JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
+                        HandleId id, HandleValue value,
+                        JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
+{
+    JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
+    js::DefineGenericOp op = obj->getOps()->defineGeneric;
+    if (op)
+        return op(cx->asJSContext(), obj, id, value, getter, setter, attrs);
+    return baseops::DefineGeneric(cx, obj, id, value, getter, setter, attrs);
+}
+
+/* static */ JSBool
+JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
+                         PropertyName *name, HandleValue value,
+                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
+{
+    RootedId id(cx, NameToId(name));
+    return defineGeneric(cx, obj, id, value, getter, setter, attrs);
+}
+
+/* static */ JSBool
+JSObject::defineSpecial(ExclusiveContext *cx, HandleObject obj,
+                        SpecialId sid, HandleValue value,
+                        JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
+{
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return defineGeneric(cx, obj, id, value, getter, setter, attrs);
+}
+
 JSBool
-baseops::DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
+baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx);
     if (index <= JSID_INT_MAX) {
         id = INT_TO_JSID(index);
         return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
     }
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     if (!IndexToId(cx, index, &id))
         return false;
 
     return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, 0, 0);
 }
 
+/* static */ JSBool
+JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
+                        uint32_t index, HandleValue value,
+                        JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
+{
+    js::DefineElementOp op = obj->getOps()->defineElement;
+    if (op)
+        return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
+    return baseops::DefineElement(cx, obj, index, value, getter, setter, attrs);
+}
+
+Shape *
+JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
+{
+    JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    RootedObject self(cx, this);
+    RootedId id(cx, idArg);
+    return addProperty(cx, self, id, NULL, NULL, slot, attrs, 0, 0);
+}
+
+Shape *
+JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
+                          uint32_t slot, unsigned attrs)
+{
+    JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+    RootedObject self(cx, this);
+    RootedId id(cx, NameToId(name));
+    return addProperty(cx, self, id, NULL, NULL, slot, attrs, 0, 0);
+}
+
 /*
  * Backward compatibility requires allowing addProperty hooks to mutate the
  * nominal initial value of a slotful property, while GC safety wants that
  * value to be stored before the call-out through the hook.  Optimize to do
  * both while saving cycles for classes that stub their addProperty hook.
  */
 static inline bool
-CallAddPropertyHook(JSContext *cx, Class *clasp, HandleObject obj, HandleShape shape,
+CallAddPropertyHook(ExclusiveContext *cx, Class *clasp, HandleObject obj, HandleShape shape,
                     HandleValue nominal)
 {
     if (clasp->addProperty != JS_PropertyStub) {
         /* Make a local copy of value so addProperty can mutate its inout parameter. */
         RootedValue value(cx, nominal);
 
         Rooted<jsid> id(cx, shape->propid());
-        if (!CallJSPropertyOp(cx, clasp->addProperty, obj, id, &value)) {
+        if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
             obj->removeProperty(cx, shape->propid());
             return false;
         }
         if (value.get() != nominal) {
             if (shape->hasSlot())
                 JSObject::nativeSetSlotWithType(cx, obj, shape, value);
         }
     }
     return true;
 }
 
 static inline bool
-CallAddPropertyHookDense(JSContext *cx, Class *clasp, HandleObject obj, uint32_t index,
+CallAddPropertyHookDense(ExclusiveContext *cx, Class *clasp, HandleObject obj, uint32_t index,
                          HandleValue nominal)
 {
     /* Inline addProperty for array objects. */
     if (obj->is<ArrayObject>()) {
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         uint32_t length = arr->length();
         if (index >= length)
-            ArrayObject::setLength(cx, arr, index + 1);
+            ArrayObject::setLength(cx->asJSContext(), arr, index + 1);
         return true;
     }
 
     if (clasp->addProperty != JS_PropertyStub) {
         /* Make a local copy of value so addProperty can mutate its inout parameter. */
         RootedValue value(cx, nominal);
 
         Rooted<jsid> id(cx, INT_TO_JSID(index));
-        if (!CallJSPropertyOp(cx, clasp->addProperty, obj, id, &value)) {
+        if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
             JSObject::setDenseElementHole(cx, obj, index);
             return false;
         }
         if (value.get() != nominal)
             JSObject::setDenseElementWithType(cx, obj, index, value);
     }
     return true;
 }
 
 static inline bool
-DefinePropertyOrElement(JSContext *cx, HandleObject obj, HandleId id,
+DefinePropertyOrElement(ExclusiveContext *cx, HandleObject obj, HandleId id,
                         PropertyOp getter, StrictPropertyOp setter,
                         unsigned attrs, unsigned flags, int shortid,
                         HandleValue value, bool callSetterAfterwards, bool setterIsStrict)
 {
     /* Use dense storage for new indexed properties where possible. */
     if (JSID_IS_INT(id) &&
         getter == JS_PropertyStub &&
         setter == JS_StrictPropertyStub &&
@@ -3418,17 +3496,17 @@ DefinePropertyOrElement(JSContext *cx, H
             obj->setDenseElementMaybeConvertDouble(index, value);
             return CallAddPropertyHookDense(cx, obj->getClass(), obj, index, value);
         }
     }
 
     if (obj->is<ArrayObject>()) {
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         if (id == NameToId(cx->names().length))
-            return ArraySetLength(cx, arr, id, attrs, value, setterIsStrict);
+            return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, setterIsStrict);
 
         uint32_t index;
         if (js_IdIsIndex(id, &index)) {
             bool definesPast;
             if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
                 return false;
             if (definesPast)
                 return true;
@@ -3462,23 +3540,23 @@ DefinePropertyOrElement(JSContext *cx, H
         }
     }
 
     if (!CallAddPropertyHook(cx, obj->getClass(), obj, shape, value))
         return false;
 
     if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
         RootedValue nvalue(cx, value);
-        return js_NativeSet(cx, obj, obj, shape, setterIsStrict, &nvalue);
+        return js_NativeSet(cx->asJSContext(), obj, obj, shape, setterIsStrict, &nvalue);
     }
     return true;
 }
 
 bool
-js::DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
+js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                          unsigned flags, int shortid, unsigned defineHow /* = 0 */)
 {
     JS_ASSERT((defineHow & ~(DNP_DONT_PURGE | DNP_SKIP_TYPE)) == 0);
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
@@ -3652,17 +3730,17 @@ CallResolveOp(JSContext *cx, HandleObjec
     else
         objp.set(NULL);
 
     return true;
 }
 
 template <AllowGC allowGC>
 static JS_ALWAYS_INLINE bool
-LookupPropertyWithFlagsInline(JSContext *cx,
+LookupPropertyWithFlagsInline(ExclusiveContext *cx,
                               typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                               typename MaybeRooted<jsid, allowGC>::HandleType id,
                               unsigned flags,
                               typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                               typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
 {
     /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's
      *     |CanEffectlesslyCallLookupGenericOnObject| logic.
@@ -3689,17 +3767,17 @@ LookupPropertyWithFlagsInline(JSContext 
             }
         }
 
         /* Try obj's class resolve hook if id was not found in obj's scope. */
         if (current->getClass()->resolve != JS_ResolveStub) {
             if (!allowGC)
                 return false;
             bool recursed;
-            if (!CallResolveOp(cx,
+            if (!CallResolveOp(cx->asJSContext(),
                                MaybeRooted<JSObject*, allowGC>::toHandle(current),
                                MaybeRooted<jsid, allowGC>::toHandle(id),
                                flags,
                                MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
                                MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
                                &recursed))
             {
                 return false;
@@ -3718,68 +3796,85 @@ LookupPropertyWithFlagsInline(JSContext 
 
         typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
 
         if (!proto)
             break;
         if (!proto->isNative()) {
             if (!allowGC)
                 return false;
-            return JSObject::lookupGeneric(cx,
+            return JSObject::lookupGeneric(cx->asJSContext(),
                                            MaybeRooted<JSObject*, allowGC>::toHandle(proto),
                                            MaybeRooted<jsid, allowGC>::toHandle(id),
                                            MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
                                            MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
         }
 
         current = proto;
     }
 
     objp.set(NULL);
     propp.set(NULL);
     return true;
 }
 
 template <AllowGC allowGC>
 JSBool
-baseops::LookupProperty(JSContext *cx,
+baseops::LookupProperty(ExclusiveContext *cx,
                         typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
                         typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                         typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
 {
     /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's
      *     |CanEffectlesslyCallLookupGenericOnObject| logic.
      *     If this changes, please remember to update the logic there as well.
      */
-    return LookupPropertyWithFlagsInline<allowGC>(cx, obj, id, cx->resolveFlags, objp, propp);
+    uint32_t resolveFlags =
+        cx->isJSContext() ? cx->asJSContext()->resolveFlags : 0;
+    return LookupPropertyWithFlagsInline<allowGC>(cx, obj, id, resolveFlags, objp, propp);
 }
 
 template JSBool
-baseops::LookupProperty<CanGC>(JSContext *cx, HandleObject obj, HandleId id,
+baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleObject obj, HandleId id,
                                MutableHandleObject objp, MutableHandleShape propp);
 
 template JSBool
-baseops::LookupProperty<NoGC>(JSContext *cx, JSObject *obj, jsid id,
+baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id,
                               FakeMutableHandle<JSObject*> objp,
                               FakeMutableHandle<Shape*> propp);
 
+/* static */ JSBool
+JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
+                        MutableHandleObject objp, MutableHandleShape propp)
+{
+    /*
+     * NB: The logic of lookupGeneric is implicitly reflected in IonBuilder.cpp's
+     *     |CanEffectlesslyCallLookupGenericOnObject| logic.
+     *     If this changes, please remember to update the logic there as well.
+     */
+    LookupGenericOp op = obj->getOps()->lookupGeneric;
+    if (op)
+        return op(cx, obj, id, objp, propp);
+    return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp);
+}
+
 JSBool
 baseops::LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                        MutableHandleObject objp, MutableHandleShape propp)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
 
     return LookupPropertyWithFlagsInline<CanGC>(cx, obj, id, cx->resolveFlags, objp, propp);
 }
 
 bool
-js::LookupPropertyWithFlags(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
+js::LookupPropertyWithFlags(ExclusiveContext *cx, HandleObject obj, HandleId id, unsigned flags,
                             MutableHandleObject objp, MutableHandleShape propp)
 {
     return LookupPropertyWithFlagsInline<CanGC>(cx, obj, id, flags, objp, propp);
 }
 
 bool
 js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
                MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
@@ -4980,17 +5075,18 @@ js::IsDelegate(JSContext *cx, HandleObje
     }
 }
 
 /*
  * The first part of this function has been hand-expanded and optimized into
  * NewBuiltinClassInstance in jsobjinlines.h.
  */
 bool
-js_GetClassPrototype(JSContext *cx, JSProtoKey protoKey, MutableHandleObject protop, Class *clasp)
+js_GetClassPrototype(ExclusiveContext *cx, JSProtoKey protoKey,
+                     MutableHandleObject protop, Class *clasp)
 {
     JS_ASSERT(JSProto_Null <= protoKey);
     JS_ASSERT(protoKey < JSProto_LIMIT);
 
     if (protoKey != JSProto_Null) {
         const Value &v = cx->global()->getReservedSlot(JSProto_LIMIT + protoKey);
         if (v.isObject()) {
             protop.set(&v.toObject());
@@ -4999,18 +5095,27 @@ js_GetClassPrototype(JSContext *cx, JSPr
     }
 
     RootedValue v(cx);
     if (!js_FindClassObject(cx, protoKey, &v, clasp))
         return false;
 
     if (IsFunctionObject(v)) {
         RootedObject ctor(cx, &v.get().toObject());
-        if (!JSObject::getProperty(cx, ctor, ctor, cx->names().classPrototype, &v))
-            return false;
+        if (cx->isJSContext()) {
+            if (!JSObject::getProperty(cx->asJSContext(),
+                                       ctor, ctor, cx->names().classPrototype, &v))
+            {
+                return false;
+            }
+        } else {
+            Shape *shape = ctor->nativeLookup(cx, cx->names().classPrototype);
+            if (!shape || !NativeGetPureInline(ctor, shape, v.address()))
+                return false;
+        }
     }
 
     protop.set(v.get().isObject() ? &v.get().toObject() : NULL);
     return true;
 }
 
 JSObject *
 PrimitiveToObject(JSContext *cx, const Value &v)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -97,48 +97,32 @@ namespace baseops {
 
 /*
  * On success, and if id was found, return true with *objp non-null and with a
  * property of *objp stored in *propp. If successful but id was not found,
  * return true with both *objp and *propp null.
  */
 template <AllowGC allowGC>
 extern JSBool
-LookupProperty(JSContext *cx,
+LookupProperty(ExclusiveContext *cx,
                typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                typename MaybeRooted<jsid, allowGC>::HandleType id,
                typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp);
 
-inline bool
-LookupProperty(JSContext *cx, HandleObject obj, PropertyName *name,
-               MutableHandleObject objp, MutableHandleShape propp)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return LookupProperty<CanGC>(cx, obj, id, objp, propp);
-}
-
 extern JSBool
 LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
               MutableHandleObject objp, MutableHandleShape propp);
 
 extern JSBool
-DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
+DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
                JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
 
-inline JSBool
-DefineProperty(JSContext *cx, HandleObject obj, PropertyName *name, HandleValue value,
-               JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return DefineGeneric(cx, obj, id, value, getter, setter, attrs);
-}
-
 extern JSBool
-DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
+DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
               JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
 
 extern JSBool
 GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp);
 
 extern JSBool
 GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp);
 
@@ -241,111 +225,112 @@ class JSObject : public js::ObjectImpl
     /* Make the type object to use for LAZY_TYPE objects. */
     static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj);
 
   public:
     /*
      * Update the last property, keeping the number of allocated slots in sync
      * with the object's new slot span.
      */
-    static bool setLastProperty(JSContext *cx, JS::HandleObject obj, js::HandleShape shape);
+    static bool setLastProperty(js::ExclusiveContext *cx,
+                                JS::HandleObject obj, js::HandleShape shape);
 
     /* As above, but does not change the slot span. */
     inline void setLastPropertyInfallible(js::Shape *shape);
 
     /*
      * Make a non-array object with the specified initial state. This method
      * takes ownership of any extantSlots it is passed.
      */
-    static inline JSObject *create(JSContext *cx,
+    static inline JSObject *create(js::ExclusiveContext *cx,
                                    js::gc::AllocKind kind,
                                    js::gc::InitialHeap heap,
                                    js::HandleShape shape,
                                    js::HandleTypeObject type,
                                    js::HeapSlot *extantSlots = NULL);
 
     /* Make an array object with the specified initial state. */
-    static inline JSObject *createArray(JSContext *cx,
-                                        js::gc::AllocKind kind,
-                                        js::gc::InitialHeap heap,
-                                        js::HandleShape shape,
-                                        js::HandleTypeObject type,
-                                        uint32_t length);
+    static inline js::ArrayObject *createArray(js::ExclusiveContext *cx,
+                                               js::gc::AllocKind kind,
+                                               js::gc::InitialHeap heap,
+                                               js::HandleShape shape,
+                                               js::HandleTypeObject type,
+                                               uint32_t length);
 
     /*
      * Remove the last property of an object, provided that it is safe to do so
      * (the shape and previous shape do not carry conflicting information about
      * the object itself).
      */
-    inline void removeLastProperty(JSContext *cx);
+    inline void removeLastProperty(js::ExclusiveContext *cx);
     inline bool canRemoveLastProperty();
 
     /*
      * Update the slot span directly for a dictionary object, and allocate
      * slots to cover the new span if necessary.
      */
-    static bool setSlotSpan(JSContext *cx, JS::HandleObject obj, uint32_t span);
+    static bool setSlotSpan(js::ExclusiveContext *cx, JS::HandleObject obj, uint32_t span);
 
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
   public:
-    bool setDelegate(JSContext *cx) {
+    bool setDelegate(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
     }
 
     bool isBoundFunction() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
     }
 
     inline bool hasSpecialEquality() const;
 
     bool watched() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
     }
-    bool setWatched(JSContext *cx) {
+    bool setWatched(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
     }
 
     /* See StackFrame::varObj. */
     inline bool isVarObj();
-    bool setVarObj(JSContext *cx) {
+    bool setVarObj(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::VAROBJ);
     }
 
     /*
      * Objects with an uncacheable proto can have their prototype mutated
      * without inducing a shape change on the object. Property cache entries
      * and JIT inline caches should not be filled for lookups across prototype
      * lookups on the object.
      */
     bool hasUncacheableProto() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO);
     }
-    bool setUncacheableProto(JSContext *cx) {
+    bool setUncacheableProto(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
     }
 
     /*
      * Whether SETLELEM was used to access this object. See also the comment near
      * PropertyTree::MAX_HEIGHT.
      */
     bool hadElementsAccess() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
-    bool setHadElementsAccess(JSContext *cx) {
+    bool setHadElementsAccess(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
 
   public:
     bool nativeEmpty() const {
         return lastProperty()->isEmptyShape();
     }
 
-    bool shadowingShapeChange(JSContext *cx, const js::Shape &shape);
+    bool shadowingShapeChange(js::ExclusiveContext *cx, const js::Shape &shape);
 
     /*
      * Whether there may be indexed properties on this object, excluding any in
      * the object's elements.
      */
     bool isIndexed() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED);
     }
@@ -379,39 +364,40 @@ class JSObject : public js::ObjectImpl
         return slot - numFixedSlots();
     }
 
     /*
      * Grow or shrink slots immediately before changing the slot span.
      * The number of allocated slots is not stored explicitly, and changes to
      * the slots must track changes in the slot span.
      */
-    static bool growSlots(JSContext *cx, js::HandleObject obj, uint32_t oldCount,
+    static bool growSlots(js::ExclusiveContext *cx, js::HandleObject obj, uint32_t oldCount,
                           uint32_t newCount);
-    static void shrinkSlots(JSContext *cx, js::HandleObject obj, uint32_t oldCount,
+    static void shrinkSlots(js::ExclusiveContext *cx, js::HandleObject obj, uint32_t oldCount,
                             uint32_t newCount);
 
     bool hasDynamicSlots() const { return slots != NULL; }
 
   protected:
-    static inline bool updateSlotsForSpan(JSContext *cx, js::HandleObject obj, size_t oldSpan,
-                                          size_t newSpan);
+    static inline bool updateSlotsForSpan(js::ExclusiveContext *cx,
+                                          js::HandleObject obj, size_t oldSpan, size_t newSpan);
 
   public:
     /*
      * Trigger the write barrier on a range of slots that will no longer be
      * reachable.
      */
     inline void prepareSlotRangeForOverwrite(size_t start, size_t end);
     inline void prepareElementRangeForOverwrite(size_t start, size_t end);
 
     void rollbackProperties(JSContext *cx, uint32_t slotSpan);
 
     inline void nativeSetSlot(uint32_t slot, const js::Value &value);
-    static inline void nativeSetSlotWithType(JSContext *cx, js::HandleObject, js::Shape *shape,
+    static inline void nativeSetSlotWithType(js::ExclusiveContext *cx,
+                                             js::HandleObject, js::Shape *shape,
                                              const js::Value &value);
 
     inline const js::Value &getReservedSlot(uint32_t index) const {
         JS_ASSERT(index < JSSLOT_FREE(getClass()));
         return getSlot(index);
     }
 
     inline js::HeapSlot &getReservedSlotRef(uint32_t index) {
@@ -421,17 +407,17 @@ class JSObject : public js::ObjectImpl
 
     inline void initReservedSlot(uint32_t index, const js::Value &v);
     inline void setReservedSlot(uint32_t index, const js::Value &v);
 
     /*
      * Marks this object as having a singleton type, and leave the type lazy.
      * Constructs a new, unique shape for the object.
      */
-    static inline bool setSingletonType(JSContext *cx, js::HandleObject obj);
+    static inline bool setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj);
 
     // uninlinedGetType() is the same as getType(), but not inlined.
     inline js::types::TypeObject* getType(JSContext *cx);
     js::types::TypeObject* uninlinedGetType(JSContext *cx);
 
     const js::HeapPtr<js::types::TypeObject> &typeFromGC() const {
         /* Direct field access for use by GC. */
         return type_;
@@ -456,28 +442,26 @@ class JSObject : public js::ObjectImpl
     }
     static inline bool getProto(JSContext *cx, js::HandleObject obj,
                                 js::MutableHandleObject protop);
 
     // uninlinedSetType() is the same as setType(), but not inlined.
     inline void setType(js::types::TypeObject *newType);
     void uninlinedSetType(js::types::TypeObject *newType);
 
-    js::types::TypeObject *getNewType(JSContext *cx, js::Class *clasp, JSFunction *fun = NULL);
-
 #ifdef DEBUG
     bool hasNewType(js::Class *clasp, js::types::TypeObject *newType);
 #endif
 
     /*
      * Mark an object that has been iterated over and is a singleton. We need
      * to recover this information in the object's type information after it
      * is purged on GC.
      */
-    bool setIteratedSingleton(JSContext *cx) {
+    bool setIteratedSingleton(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::ITERATED_SINGLETON);
     }
 
     /*
      * Mark an object as requiring its default 'new' type to have unknown
      * properties.
      */
     static bool setNewTypeUnknown(JSContext *cx, js::Class *clasp, JS::HandleObject obj);
@@ -573,78 +557,82 @@ class JSObject : public js::ObjectImpl
     static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, FREEZE, resultp);
     }
 
     /* toString support. */
     static const char *className(JSContext *cx, js::HandleObject obj);
 
     /* Accessors for elements. */
-    inline bool ensureElements(JSContext *cx, uint32_t capacity);
+    inline bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity);
     bool growElements(js::ThreadSafeContext *tcx, uint32_t newcap);
-    void shrinkElements(JSContext *cx, uint32_t cap);
+    void shrinkElements(js::ThreadSafeContext *cx, uint32_t cap);
     void setDynamicElements(js::ObjectElements *header) {
         JS_ASSERT(!hasDynamicElements());
         elements = header->elements();
         JS_ASSERT(hasDynamicElements());
     }
 
     uint32_t getDenseCapacity() {