Bug 938124 - Add mprotect mechanism indicating which GC heap accesses made during Ion compilation are threadsafe, r=jandem.
☠☠ backed out by 8fefcd2bb154 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 19 Nov 2013 15:14:30 -0700
changeset 171115 00644e4b067d981d77fcffef244a09bbc0896016
parent 171114 957acbe8ca22218af9a48bf94bfa0a94833c9694
child 171116 de3dd3c48114149af5c7efcab0219749a5299ca9
push idunknown
push userunknown
push dateunknown
reviewersjandem
bugs938124
milestone28.0a1
Bug 938124 - Add mprotect mechanism indicating which GC heap accesses made during Ion compilation are threadsafe, r=jandem.
js/src/builtin/Eval.cpp
js/src/builtin/Eval.h
js/src/gc/Barrier.h
js/src/gc/Heap.h
js/src/jit/BaselineFrame.h
js/src/jit/BaselineInspector.h
js/src/jit/BytecodeAnalysis.cpp
js/src/jit/BytecodeAnalysis.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CompileInfo.h
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonCode.h
js/src/jit/IonMacroAssembler.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MIRGraph.h
js/src/jit/ParallelFunctions.cpp
js/src/jit/Snapshots.cpp
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jsanalyze.h
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsatominlines.h
js/src/jsbool.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartmentinlines.h
js/src/jsfun.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.cpp
js/src/jsopcodeinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/shell/js.cpp
js/src/vm/GlobalObject.h
js/src/vm/ObjectImpl.cpp
js/src/vm/ObjectImpl.h
js/src/vm/RegExpObject.h
js/src/vm/Runtime-inl.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
js/src/vm/String.h
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -427,16 +427,22 @@ js::DirectEval(JSContext *cx, const Call
 
 bool
 js::IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
 {
     return scopeChain->global().getOriginalEval() == v;
 }
 
 bool
+js::IsBuiltinEvalForScope(GlobalObject *global, const Value &v)
+{
+    return global->getOriginalEval() == v;
+}
+
+bool
 js::IsAnyBuiltinEval(JSFunction *fun)
 {
     return fun->maybeNative() == IndirectEval;
 }
 
 JSPrincipals *
 js::PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx)
 {
--- a/js/src/builtin/Eval.h
+++ b/js/src/builtin/Eval.h
@@ -33,16 +33,21 @@ DirectEvalFromIon(JSContext *cx,
                   HandleValue thisValue, HandleString str,
                   jsbytecode * pc, MutableHandleValue vp);
 
 // True iff 'v' is the built-in eval function for the global object that
 // corresponds to 'scopeChain'.
 extern bool
 IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v);
 
+class GlobalObject;
+
+extern bool
+IsBuiltinEvalForScope(GlobalObject *global, const Value &v);
+
 // True iff fun is a built-in eval function.
 extern bool
 IsAnyBuiltinEval(JSFunction *fun);
 
 // Return the principals to assign to code compiled for a call to
 // eval or the Function constructor.
 extern JSPrincipals *
 PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx);
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -154,16 +154,19 @@ class BarrieredCell : public gc::Cell
     JS_ALWAYS_INLINE JS::shadow::Zone *shadowZone() const { return JS::shadow::Zone::asShadowZone(zone()); }
     JS_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const { return tenuredZoneFromAnyThread(); }
     JS_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static JS_ALWAYS_INLINE void readBarrier(T *thing) {
 #ifdef JSGC_INCREMENTAL
+        // Off thread Ion compilation never occurs when barriers are active.
+        AutoUnprotectCell unprotect(thing);
+
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "read barrier");
             JS_ASSERT(tmp == thing);
         }
 #endif
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -988,22 +988,16 @@ Cell::runtimeFromAnyThread() const
 }
 
 inline JS::shadow::Runtime *
 Cell::shadowRuntimeFromAnyThread() const
 {
     return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread());
 }
 
-AllocKind
-Cell::tenuredGetAllocKind() const
-{
-    return arenaHeader()->getAllocKind();
-}
-
 bool
 Cell::isMarked(uint32_t color /* = BLACK */) const
 {
     JS_ASSERT(isTenured());
     AssertValidColor(this, color);
     return chunk()->bitmap.isMarked(this, color);
 }
 
@@ -1109,11 +1103,47 @@ InFreeList(ArenaHeader *aheader, void *t
          * The last possible empty span is an the end of the arena. Here
          * span->end < thing < thingsEnd and so we must have more spans.
          */
         span = span->nextSpan();
     }
 }
 
 } /* namespace gc */
+
+// Ion compilation mainly occurs off the main thread, and may run while the
+// main thread is performing arbitrary VM operations, excepting GC activity.
+// The below class is used to mark functions and other operations which can
+// safely be performed off thread without racing. When running with thread
+// safety checking on, any access to a GC thing outside of an AutoUnprotectCell
+// will cause an access violation.
+class AutoUnprotectCell
+{
+public:
+#if defined(DEBUG) && !defined(XP_WIN)
+    JSRuntime *runtime;
+    gc::ArenaHeader *arena;
+
+    AutoUnprotectCell(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+    ~AutoUnprotectCell();
+#else
+    AutoUnprotectCell(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+    ~AutoUnprotectCell() {}
+#endif
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+typedef AutoUnprotectCell AutoUnprotectCellUnderCompilationLock;
+typedef AutoUnprotectCell AutoUnprotectCellForWrite;
+
+gc::AllocKind
+gc::Cell::tenuredGetAllocKind() const
+{
+    AutoUnprotectCell unprotect(this);
+    return arenaHeader()->getAllocKind();
+}
+
 } /* namespace js */
 
 #endif /* gc_Heap_h */
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -147,17 +147,17 @@ class BaselineFrame
     }
     Value *valueSlot(size_t slot) const {
         JS_ASSERT(slot < numValueSlots());
         return (Value *)this - (slot + 1);
     }
 
     Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
-        JS_ASSERT(i < script()->nfixed);
+        JS_ASSERT(i < script()->getNfixed());
         return *valueSlot(i);
     }
 
     Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         JS_ASSERT(i < numFormalArgs());
         JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
                                     !script()->formalIsAliased(i));
         return argv()[i];
@@ -178,17 +178,17 @@ class BaselineFrame
     }
 
     unsigned numActualArgs() const {
         return *(size_t *)(reinterpret_cast<const uint8_t *>(this) +
                              BaselineFrame::Size() +
                              offsetOfNumActualArgs());
     }
     unsigned numFormalArgs() const {
-        return script()->function()->nargs;
+        return script()->function()->getNargs();
     }
     Value &thisValue() const {
         return *(Value *)(reinterpret_cast<const uint8_t *>(this) +
                          BaselineFrame::Size() +
                          offsetOfThis());
     }
     Value *argv() const {
         return (Value *)(reinterpret_cast<const uint8_t *>(this) +
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -62,24 +62,24 @@ class BaselineInspector
 
     BaselineScript *baselineScript() const {
         return script->baselineScript();
     }
 
   private:
 #ifdef DEBUG
     bool isValidPC(jsbytecode *pc) {
-        return (pc >= script->code) && (pc < script->code + script->length);
+        return (pc >= script->getCode()) && (pc < script->getCode() + script->getLength());
     }
 #endif
 
     ICEntry &icEntryFromPC(jsbytecode *pc) {
         JS_ASSERT(hasBaselineScript());
         JS_ASSERT(isValidPC(pc));
-        ICEntry &ent = baselineScript()->icEntryFromPCOffset(pc - script->code, prevLookedUpEntry);
+        ICEntry &ent = baselineScript()->icEntryFromPCOffset(pc - script->getCode(), prevLookedUpEntry);
         JS_ASSERT(ent.isForOp());
         prevLookedUpEntry = &ent;
         return ent;
     }
 
     template <typename ICInspectorType>
     ICInspectorType makeICInspector(jsbytecode *pc, ICStub::Kind expectedFallbackKind) {
         ICEntry *ent = nullptr;
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -37,43 +37,43 @@ struct CatchFinallyRange
     bool contains(uint32_t offset) const {
         return start <= offset && offset < end;
     }
 };
 
 bool
 BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
 {
-    if (!infos_.growByUninitialized(script_->length))
+    if (!infos_.growByUninitialized(script_->getLength()))
         return false;
 
-    jsbytecode *end = script_->code + script_->length;
+    jsbytecode *end = script_->getCode() + script_->getLength();
 
     // Clear all BytecodeInfo.
     mozilla::PodZero(infos_.begin(), infos_.length());
     infos_[0].init(/*stackDepth=*/0);
 
     Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges(alloc);
 
-    for (jsbytecode *pc = script_->code; pc < end; pc += GetBytecodeLength(pc)) {
+    for (jsbytecode *pc = script_->getCode(); pc < end; pc += GetBytecodeLength(pc)) {
         JSOp op = JSOp(*pc);
-        unsigned offset = pc - script_->code;
+        unsigned offset = pc - script_->getCode();
 
         IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
-                int(pc - script_->code), int(end - script_->code), js_CodeName[op]);
+                int(pc - script_->getCode()), int(end - script_->getCode()), js_CodeName[op]);
 
         // If this bytecode info has not yet been initialized, it's not reachable.
         if (!infos_[offset].initialized)
             continue;
 
 
         unsigned stackDepth = infos_[offset].stackDepth;
 #ifdef DEBUG
         for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
-            JS_ASSERT(!infos_[chkpc - script_->code].initialized);
+            JS_ASSERT(!infos_[chkpc - script_->getCode()].initialized);
 #endif
 
         unsigned nuses = GetUseCount(script_, offset);
         unsigned ndefs = GetDefCount(script_, offset);
 
         JS_ASSERT(stackDepth >= nuses);
         stackDepth -= nuses;
         stackDepth += ndefs;
@@ -103,17 +103,17 @@ BytecodeAnalysis::init(TempAllocator &al
             }
             break;
           }
 
           case JSOP_TRY: {
             JSTryNote *tn = script_->trynotes()->vector;
             JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script_->mainOffset + tn->start;
+                unsigned startOffset = script_->getMainOffset() + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     if (tn->kind != JSTRY_ITER) {
                         infos_[catchOffset].init(stackDepth);
                         infos_[catchOffset].jumpTarget = true;
                     }
                 }
@@ -129,17 +129,17 @@ BytecodeAnalysis::init(TempAllocator &al
 
             jsbytecode *afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry);
             JS_ASSERT(afterTry > endOfTry);
 
             // Pop CatchFinallyRanges that are no longer needed.
             while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset)
                 catchFinallyRanges.popBack();
 
-            CatchFinallyRange range(endOfTry - script_->code, afterTry - script_->code);
+            CatchFinallyRange range(endOfTry - script_->getCode(), afterTry - script_->getCode());
             if (!catchFinallyRanges.append(range))
                 return false;
             break;
           }
 
           case JSOP_LOOPENTRY:
             for (size_t i = 0; i < catchFinallyRanges.length(); i++) {
                 if (catchFinallyRanges[i].contains(offset))
@@ -186,24 +186,24 @@ BytecodeAnalysis::init(TempAllocator &al
 
             // If this is a a backedge to an un-analyzed segment, analyze from there.
             bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
 
             infos_[targetOffset].init(newStackDepth);
             infos_[targetOffset].jumpTarget = true;
 
             if (jumpBack)
-                pc = script_->code + targetOffset;
+                pc = script_->getCode() + targetOffset;
         }
 
         // Handle any fallthrough from this opcode.
         if (BytecodeFallsThrough(op)) {
             jsbytecode *nextpc = pc + GetBytecodeLength(pc);
             JS_ASSERT(nextpc < end);
-            unsigned nextOffset = nextpc - script_->code;
+            unsigned nextOffset = nextpc - script_->getCode();
 
             infos_[nextOffset].init(stackDepth);
 
             if (jump)
                 infos_[nextOffset].jumpFallthrough = true;
 
             // Treat the fallthrough of a branch instruction as a jump target.
             if (jump)
--- a/js/src/jit/BytecodeAnalysis.h
+++ b/js/src/jit/BytecodeAnalysis.h
@@ -45,23 +45,23 @@ class BytecodeAnalysis
     bool hasSetArg_;
 
   public:
     explicit BytecodeAnalysis(TempAllocator &alloc, JSScript *script);
 
     bool init(TempAllocator &alloc, GSNCache &gsn);
 
     BytecodeInfo &info(jsbytecode *pc) {
-        JS_ASSERT(infos_[pc - script_->code].initialized);
-        return infos_[pc - script_->code];
+        JS_ASSERT(infos_[pc - script_->getCode()].initialized);
+        return infos_[pc - script_->getCode()];
     }
 
     BytecodeInfo *maybeInfo(jsbytecode *pc) {
-        if (infos_[pc - script_->code].initialized)
-            return &infos_[pc - script_->code];
+        if (infos_[pc - script_->getCode()].initialized)
+            return &infos_[pc - script_->getCode()];
         return nullptr;
     }
 
     bool usesScopeChain() const {
         return usesScopeChain_;
     }
 
     bool hasTryFinally() const {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -817,17 +817,17 @@ CodeGenerator::emitLambdaInit(const Regi
     // 16-bit writes.
     union {
         struct S {
             uint16_t nargs;
             uint16_t flags;
         } s;
         uint32_t word;
     } u;
-    u.s.nargs = info.fun->nargs;
+    u.s.nargs = info.nargs;
     u.s.flags = info.flags & ~JSFunction::EXTENDED;
 
     JS_STATIC_ASSERT(offsetof(JSFunction, flags) == offsetof(JSFunction, nargs) + 2);
     masm.store32(Imm32(u.word), Address(output, offsetof(JSFunction, nargs)));
     masm.storePtr(ImmGCPtr(info.scriptOrLazyScript),
                   Address(output, JSFunction::offsetOfNativeOrScript()));
     masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
@@ -2031,17 +2031,17 @@ CodeGenerator::visitCallKnown(LCallKnown
     uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
     DebugOnly<JSFunction *> target = call->getSingleTarget();
     ExecutionMode executionMode = gen->info().executionMode();
     Label end, uncompiled;
 
     // Native single targets are handled by LCallNative.
     JS_ASSERT(!target->isNative());
     // Missing arguments must have been explicitly appended by the IonBuilder.
-    JS_ASSERT(target->nargs <= call->numStackArgs());
+    JS_ASSERT(target->getNargs() <= call->numStackArgs());
 
     JS_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor());
 
     masm.checkStackAlignment();
 
     // The calleereg is known to be a non-native function, but might point to
     // a LazyScript instead of a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
@@ -2275,17 +2275,17 @@ CodeGenerator::visitApplyArgsGeneric(LAp
         Label underflow, rejoin;
 
         // Check whether the provided arguments satisfy target argc.
         if (!apply->hasSingleTarget()) {
             masm.load16ZeroExtend(Address(calleereg, offsetof(JSFunction, nargs)), copyreg);
             masm.cmp32(argcreg, copyreg);
             masm.j(Assembler::Below, &underflow);
         } else {
-            masm.cmp32(argcreg, Imm32(apply->getSingleTarget()->nargs));
+            masm.cmp32(argcreg, Imm32(apply->getSingleTarget()->getNargs()));
             masm.j(Assembler::Below, &underflow);
         }
 
         // Skip the construction of the rectifier frame because we have no
         // underflow.
         masm.jump(&rejoin);
 
         // Argument fixup needed. Get ready to call the argumentsRectifier.
@@ -3244,16 +3244,17 @@ static const VMFunction NewCallObjectInf
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register obj = ToRegister(lir->output());
 
     JSObject *templateObj = lir->mir()->templateObject();
+    AutoUnprotectCell unprotect(templateObj);
 
     // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool;
     if (lir->slots()->isRegister()) {
         ool = oolCallVM(NewCallObjectInfo, lir,
                         (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
                                     ImmGCPtr(templateObj->lastProperty()),
                                     ImmGCPtr(templateObj->hasLazyType() ? nullptr : templateObj->type()),
@@ -5738,17 +5739,17 @@ CodeGenerator::generateAsmJS()
     return true;
 }
 
 bool
 CodeGenerator::generate()
 {
     IonSpew(IonSpew_Codegen, "# Emitting code for script %s:%d",
             gen->info().script()->filename(),
-            gen->info().script()->lineno);
+            gen->info().script()->getLineno());
 
     if (!safepoints_.init(gen->alloc(), graph.totalSlotCount()))
         return false;
 
 #if JS_TRACE_LOGGING
     masm.tracelogStart(gen->info().script());
     masm.tracelogLog(TraceLogging::INFO_ENGINE_IONMONKEY);
 #endif
@@ -6544,17 +6545,17 @@ static const VMFunction DeletePropertyNo
     FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
 
 bool
 CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->name()));
     pushArg(ToValue(lir, LCallDeleteProperty::Value));
 
-    if (lir->mir()->block()->info().script()->strict)
+    if (lir->mir()->block()->info().script()->getStrict())
         return callVM(DeletePropertyStrictInfo, lir);
 
     return callVM(DeletePropertyNonStrictInfo, lir);
 }
 
 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
 static const VMFunction DeleteElementStrictInfo =
     FunctionInfo<DeleteElementFn>(DeleteElement<true>);
@@ -6562,17 +6563,17 @@ static const VMFunction DeleteElementNon
     FunctionInfo<DeleteElementFn>(DeleteElement<false>);
 
 bool
 CodeGenerator::visitCallDeleteElement(LCallDeleteElement *lir)
 {
     pushArg(ToValue(lir, LCallDeleteElement::Index));
     pushArg(ToValue(lir, LCallDeleteElement::Value));
 
-    if (lir->mir()->block()->info().script()->strict)
+    if (lir->mir()->block()->info().script()->getStrict())
         return callVM(DeleteElementStrictInfo, lir);
 
     return callVM(DeleteElementNonStrictInfo, lir);
 }
 
 bool
 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
 {
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -10,17 +10,17 @@
 #include "jsfun.h"
 
 #include "jit/Registers.h"
 
 namespace js {
 namespace jit {
 
 inline unsigned
-StartArgSlot(JSScript *script, JSFunction *fun)
+StartArgSlot(JSScript *script)
 {
     // Reserved slots:
     // Slot 0: Scope chain.
     // Slot 1: Return value.
 
     // When needed:
     // Slot 2: Argumentsobject.
 
@@ -32,17 +32,17 @@ inline unsigned
 CountArgSlots(JSScript *script, JSFunction *fun)
 {
     // Slot x + 0: This value.
     // Slot x + 1: Argument 1.
     // ...
     // Slot x + n: Argument n.
 
     // Note: when updating this, please also update the assert in SnapshotWriter::startFrame
-    return StartArgSlot(script, fun) + (fun ? fun->nargs + 1 : 0);
+    return StartArgSlot(script) + (fun ? fun->getNargs() + 1 : 0);
 }
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
     CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
                 ExecutionMode executionMode)
@@ -53,21 +53,21 @@ class CompileInfo
 
         // The function here can flow in from anywhere so look up the canonical function to ensure that
         // we do not try to embed a nursery pointer in jit-code.
         if (fun_) {
             fun_ = fun_->nonLazyScript()->function();
             JS_ASSERT(fun_->isTenured());
         }
 
-        nimplicit_ = StartArgSlot(script, fun)              /* scope chain and argument obj */
+        nimplicit_ = StartArgSlot(script)                   /* scope chain and argument obj */
                    + (fun ? 1 : 0);                         /* this */
-        nargs_ = fun ? fun->nargs : 0;
-        nlocals_ = script->nfixed;
-        nstack_ = script->nslots - script->nfixed;
+        nargs_ = fun ? fun->getNargs() : 0;
+        nlocals_ = script->getNfixed();
+        nstack_ = script->getNslots() - script->getNfixed();
         nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
     }
 
     CompileInfo(unsigned nlocals, ExecutionMode executionMode)
       : script_(nullptr), fun_(nullptr), osrPc_(nullptr), constructing_(false),
         executionMode_(executionMode)
     {
         nimplicit_ = 0;
@@ -91,20 +91,20 @@ class CompileInfo
     }
 
     bool hasOsrAt(jsbytecode *pc) {
         JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
         return pc == osrPc();
     }
 
     jsbytecode *startPC() const {
-        return script_->code;
+        return script_->getCode();
     }
     jsbytecode *limitPC() const {
-        return script_->code + script_->length;
+        return script_->getCode() + script_->getLength();
     }
 
     const char *filename() const {
         return script_->filename();
     }
 
     unsigned lineno() const {
         return script_->lineno;
@@ -204,17 +204,17 @@ class CompileInfo
         return firstLocalSlot() + nlocals();
     }
     uint32_t stackSlot(uint32_t i) const {
         return firstStackSlot() + i;
     }
 
     uint32_t startArgSlot() const {
         JS_ASSERT(script());
-        return StartArgSlot(script(), fun());
+        return StartArgSlot(script());
     }
     uint32_t endArgSlot() const {
         JS_ASSERT(script());
         return CountArgSlots(script(), fun());
     }
 
     uint32_t totalSlots() const {
         JS_ASSERT(script() && fun());
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -41,16 +41,18 @@
 #include "jit/ValueNumbering.h"
 #include "vm/ForkJoin.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
+using mozilla::Maybe;
+
 using namespace js;
 using namespace js::jit;
 
 // Global variables.
 IonOptions jit::js_IonOptions;
 
 // Assert that IonCode is gc::Cell aligned.
 JS_STATIC_ASSERT(sizeof(IonCode) % gc::CellSize == 0);
@@ -1270,17 +1272,17 @@ OptimizeMIR(MIRGenerator *mir)
     if (mir->shouldCancel("UCE"))
         return false;
 
     if (js_IonOptions.licm) {
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
         JSScript *script = mir->info().script();
-        if (!script || !script->hadFrequentBailouts) {
+        if (!script || !script->getHadFrequentBailouts()) {
             LICM licm(mir, graph);
             if (!licm.analyze())
                 return false;
             IonSpewPass("LICM");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
@@ -1547,19 +1549,19 @@ OffThreadCompilationAvailable(JSContext 
     // Even if off thread compilation is enabled, compilation must still occur
     // on the main thread in some cases. Do not compile off thread during an
     // incremental GC, as this may trip incremental read barriers.
     //
     // Skip off thread compilation if PC count profiling is enabled, as
     // CodeGenerator::maybeCreateScriptCounts will not attach script profiles
     // when running off thread.
     //
-    // Also skip off thread compilation if the SPS profiler is enabled, as it
-    // stores strings in the spsProfiler data structure, which is not protected
-    // by a lock.
+    // Skip off thread compilation if the SPS profiler is enabled, as it stores
+    // strings in the spsProfiler data structure, which is not protected by a
+    // lock.
     return OffThreadIonCompilationEnabled(cx->runtime())
         && cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL
         && !cx->runtime()->profilingScripts
         && !cx->runtime()->spsProfiler.enabled();
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
@@ -1652,16 +1654,25 @@ IonCompile(JSContext *cx, JSScript *scri
         return AbortReason_Alloc;
 
     JS_ASSERT(!GetIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
+    Maybe<AutoProtectHeapForCompilation> protect;
+    if (js_IonOptions.checkThreadSafety &&
+        cx->runtime()->gcIncrementalState == gc::NO_INCREMENTAL &&
+        !cx->runtime()->profilingScripts &&
+        !cx->runtime()->spsProfiler.enabled())
+    {
+        protect.construct(cx->runtime());
+    }
+
     bool succeeded = builder->build();
     builder->clearForBackEnd();
 
     if (!succeeded) {
         if (cx->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Builder raised exception.");
             return AbortReason_Error;
         }
@@ -1687,16 +1698,19 @@ IonCompile(JSContext *cx, JSScript *scri
     }
 
     ScopedJSDeletePtr<CodeGenerator> codegen(CompileBackEnd(builder));
     if (!codegen) {
         IonSpew(IonSpew_Abort, "Failed during back-end compilation.");
         return AbortReason_Disable;
     }
 
+    if (!protect.empty())
+        protect.destroy();
+
     bool success = codegen->link(cx, builder->constraints());
 
     IonSpewEndFunction();
 
     return success ? AbortReason_NoAbort : AbortReason_Disable;
 }
 
 static bool
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -78,16 +78,22 @@ struct IonOptions
     bool rangeAnalysis;
 
     // Whether to enable extra code to perform dynamic validation of
     // RangeAnalysis results.
     //
     // Default: false
     bool checkRangeAnalysis;
 
+    // Whether to protect the GC heap during Ion compilation and ensure that
+    // only threadsafe operations are performed on it.
+    //
+    // Default: false
+    bool checkThreadSafety;
+
     // Whether to perform expensive graph-consistency DEBUG-only assertions.
     // It can be useful to disable this to reduce DEBUG-compile time of large
     // asm.js programs.
     //
     // Default: true
     bool assertGraphConsistency;
 
     // Toggles whether Unreachable Code Elimination is performed.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -60,18 +60,18 @@ IonBuilder::IonBuilder(JSContext *analys
     loops_(*temp),
     switches_(*temp),
     labels_(*temp),
     iterators_(*temp),
     loopHeaders_(*temp),
     inspector(inspector),
     inliningDepth_(inliningDepth),
     numLoopRestarts_(0),
-    failedBoundsCheck_(info->script()->failedBoundsCheck),
-    failedShapeGuard_(info->script()->failedShapeGuard),
+    failedBoundsCheck_(info->script()->getFailedBoundsCheck()),
+    failedShapeGuard_(info->script()->getFailedShapeGuard()),
     nonStringIteration_(false),
     lazyArguments_(nullptr),
     inlineCallInfo_(nullptr)
 {
     script_.init(info->script());
     pc = info->startPC();
 
     JS_ASSERT(script()->hasBaselineScript());
@@ -213,22 +213,22 @@ IonBuilder::getPolyCallTargets(types::Te
             if (!obj->is<JSFunction>()) {
                 targets.clear();
                 return true;
             }
             fun = &obj->as<JSFunction>();
         } else {
             types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
             JS_ASSERT(typeObj);
-            if (!typeObj->interpretedFunction) {
+            if (!typeObj->getInterpretedFunction()) {
                 targets.clear();
                 return true;
             }
 
-            fun = typeObj->interpretedFunction;
+            fun = typeObj->getInterpretedFunction();
             *gotLambda = true;
         }
 
         // Don't optimize if we're constructing and the callee is not a
         // constructor, so that CallKnown does not have to handle this case
         // (it should always throw).
         if (constructing && !fun->isInterpretedConstructor() && !fun->isNativeConstructor()) {
             targets.clear();
@@ -249,26 +249,23 @@ IonBuilder::getPolyCallTargets(types::Te
 bool
 IonBuilder::canEnterInlinedFunction(JSFunction *target)
 {
     if (target->isHeavyweight())
         return false;
 
     JSScript *targetScript = target->nonLazyScript();
 
-    if (targetScript->uninlineable)
-        return false;
-
-    if (!targetScript->analyzedArgsUsage())
+    if (targetScript->getUninlineable())
         return false;
 
     if (targetScript->needsArgsObj())
         return false;
 
-    if (!targetScript->compileAndGo)
+    if (!targetScript->getCompileAndGo())
         return false;
 
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     if (targetType->unknownProperties())
         return false;
 
     return true;
 }
@@ -276,21 +273,16 @@ IonBuilder::canEnterInlinedFunction(JSFu
 bool
 IonBuilder::canInlineTarget(JSFunction *target, CallInfo &callInfo)
 {
     if (!target->isInterpreted()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
         return false;
     }
 
-    if (target->getParent() != &script()->global()) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
-        return false;
-    }
-
     // Allow constructing lazy scripts when performing the definite properties
     // analysis, as baseline has not been used to warm the caller up yet.
     if (target->isInterpreted() && info().executionMode() == DefinitePropertiesAnalysis) {
         if (!target->getOrCreateScript(analysisContext))
             return false;
 
         RootedScript script(analysisContext, target->nonLazyScript());
         if (!script->hasBaselineScript() && script->canBaselineCompile()) {
@@ -306,57 +298,53 @@ IonBuilder::canInlineTarget(JSFunction *
     }
 
     if (callInfo.constructing() && !target->isInterpretedConstructor()) {
         IonSpew(IonSpew_Inlining, "Cannot inline because callee is not a constructor");
         return false;
     }
 
     JSScript *inlineScript = target->nonLazyScript();
+
+    IonSpew(IonSpew_Inlining, "Trying to inline %s:%d", inlineScript->filename(), inlineScript->getLineno());
+
     ExecutionMode executionMode = info().executionMode();
     if (!CanIonCompile(inlineScript, executionMode)) {
-        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline due to disable Ion compilation",
-                                  inlineScript->filename(), inlineScript->lineno);
+        IonSpew(IonSpew_Inlining, "Cannot inline due to disable Ion compilation");
         return false;
     }
 
     // Don't inline functions which don't have baseline scripts.
     if (!inlineScript->hasBaselineScript()) {
-        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline target with no baseline jitcode",
-                                  inlineScript->filename(), inlineScript->lineno);
-        return false;
-    }
-
-    if (TooManyArguments(target->nargs)) {
-        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline too many args",
-                                  inlineScript->filename(), inlineScript->lineno);
+        IonSpew(IonSpew_Inlining, "Cannot inline target with no baseline jitcode");
+        return false;
+    }
+
+    if (TooManyArguments(target->getNargs())) {
+        IonSpew(IonSpew_Inlining, "Cannot inline too many args");
         return false;
     }
 
     if (TooManyArguments(callInfo.argc())) {
-        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline too many args",
-                                  inlineScript->filename(), inlineScript->lineno);
+        IonSpew(IonSpew_Inlining, "Cannot inline too many args");
         return false;
     }
 
     // Allow inlining of recursive calls, but only one level deep.
     IonBuilder *builder = callerBuilder_;
     while (builder) {
         if (builder->script() == inlineScript) {
-            IonSpew(IonSpew_Inlining, "%s:%d Not inlining recursive call",
-                                       inlineScript->filename(), inlineScript->lineno);
+            IonSpew(IonSpew_Inlining, "Not inlining recursive call");
             return false;
         }
         builder = builder->callerBuilder_;
     }
 
     if (!canEnterInlinedFunction(target)) {
-        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline due to oracle veto %d",
-                                  inlineScript->filename(), inlineScript->lineno,
-                                  script()->lineno);
+        IonSpew(IonSpew_Inlining, "Cannot inline due to oracle veto %d");
         return false;
     }
 
     return true;
 }
 
 void
 IonBuilder::popCfgStack()
@@ -560,17 +548,18 @@ IonBuilder::build()
     if (!init())
         return false;
 
     setCurrentAndSpecializePhis(newBlock(pc));
     if (!current)
         return false;
 
     IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d)",
-            script()->filename(), script()->lineno, (void *)script(), (int)script()->getUseCount());
+            script()->filename(), script()->getLineno(),
+            (void *)script(), (int)script()->getUseCount());
 
     if (!initParameters())
         return false;
 
     // Initialize local variables.
     for (uint32_t i = 0; i < info().nlocals(); i++) {
         MConstant *undef = MConstant::New(alloc(), UndefinedValue());
         current->add(undef);
@@ -702,17 +691,17 @@ IonBuilder::buildInline(IonBuilder *call
                         CallInfo &callInfo)
 {
     if (!init())
         return false;
 
     inlineCallInfo_ = &callInfo;
 
     IonSpew(IonSpew_Scripts, "Inlining script %s:%d (%p)",
-            script()->filename(), script()->lineno, (void *)script());
+            script()->filename(), script()->getLineno(), (void *)script());
 
     callerBuilder_ = callerBuilder;
     callerResumePoint_ = callerResumePoint;
 
     if (callerBuilder->failedBoundsCheck_)
         failedBoundsCheck_ = true;
 
     if (callerBuilder->failedShapeGuard_)
@@ -900,17 +889,17 @@ IonBuilder::initScopeChain(MDefinition *
     // to be passed in.
     if (!info().needsArgsObj() && !analysis().usesScopeChain())
         return true;
 
     // The scope chain is only tracked in scripts that have NAME opcodes which
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
-    if (!script()->compileAndGo)
+    if (!script()->getCompileAndGo())
         return abort("non-CNG global scripts are not supported");
 
     if (JSFunction *fun = info().fun()) {
         if (!callee) {
             MCallee *calleeIns = MCallee::New(alloc());
             current->add(calleeIns);
             callee = calleeIns;
         }
@@ -936,18 +925,17 @@ IonBuilder::initScopeChain(MDefinition *
 
     current->setScopeChain(scope);
     return true;
 }
 
 bool
 IonBuilder::initArgumentsObject()
 {
-    IonSpew(IonSpew_MIR, "%s:%d - Emitting code to initialize arguments object! block=%p",
-                              script()->filename(), script()->lineno, current);
+    IonSpew(IonSpew_MIR, "Emitting code to initialize arguments object! block=%p", current);
     JS_ASSERT(info().needsArgsObj());
     MCreateArgumentsObject *argsObj = MCreateArgumentsObject::New(alloc(), current->scopeChain());
     current->add(argsObj);
     current->setArgumentsObject(argsObj);
     return true;
 }
 
 bool
@@ -1182,17 +1170,17 @@ IonBuilder::traverseBytecode()
         //   (1) Have the Folded flag set on them.
         //   (2) Have more uses than before compiling this op (the value is
         //       used as operand of a new MIR instruction).
         //
         // This is used to catch problems where IonBuilder pops a value without
         // adding any SSA uses and doesn't call setFoldedUnchecked on it.
         Vector<MDefinition *, 4, IonAllocPolicy> popped(alloc());
         Vector<size_t, 4, IonAllocPolicy> poppedUses(alloc());
-        unsigned nuses = GetUseCount(script_, pc - script_->code);
+        unsigned nuses = GetUseCount(script_, pc - script_->getCode());
 
         for (unsigned i = 0; i < nuses; i++) {
             MDefinition *def = current->peek(-int32_t(i + 1));
             if (!popped.append(def) || !poppedUses.append(def->defUseCount()))
                 return false;
         }
 #endif
 
@@ -1667,17 +1655,17 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_ENDITER:
         return jsop_iterend();
 
       case JSOP_IN:
         return jsop_in();
 
       case JSOP_SETRVAL:
-        JS_ASSERT(!script()->noScriptRval);
+        JS_ASSERT(!script()->getNoScriptRval());
         current->setSlot(info().returnValueSlot(), current->pop());
         return true;
 
       case JSOP_INSTANCEOF:
         return jsop_instanceof();
 
       default:
 #ifdef DEBUG
@@ -3468,17 +3456,17 @@ IonBuilder::processReturn(JSOp op)
     switch (op) {
       case JSOP_RETURN:
         // Return the last instruction.
         def = current->pop();
         break;
 
       case JSOP_RETRVAL:
         // Return undefined eagerly if script doesn't use return value.
-        if (script()->noScriptRval) {
+        if (script()->getNoScriptRval()) {
             MInstruction *ins = MConstant::New(alloc(), UndefinedValue());
             current->add(ins);
             def = ins;
             break;
         }
 
         def = current->getSlot(info().returnValueSlot());
         break;
@@ -3781,27 +3769,31 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Pop formals again, except leave |fun| on stack for duration of call.
     callInfo.popFormals(current);
     current->push(callInfo.fun());
 
     JSScript *calleeScript = target->nonLazyScript();
     BaselineInspector inspector(calleeScript);
 
-    // Improve type information of |this| when not set.
-    if (callInfo.constructing() &&
-        !callInfo.thisArg()->resultTypeSet() &&
-        calleeScript->types)
     {
-        types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
-        if (!types->unknown()) {
-            MTypeBarrier *barrier =
-                MTypeBarrier::New(alloc(), callInfo.thisArg(), types->clone(alloc_->lifoAlloc()));
-            current->add(barrier);
-            callInfo.setThis(barrier);
+        AutoUnprotectCellUnderCompilationLock unprotect(calleeScript);
+
+        // Improve type information of |this| when not set.
+        if (callInfo.constructing() &&
+            !callInfo.thisArg()->resultTypeSet() &&
+            calleeScript->types)
+        {
+            types::StackTypeSet *types = types::TypeScript::ThisTypes(calleeScript);
+            if (!types->unknown()) {
+                MTypeBarrier *barrier =
+                    MTypeBarrier::New(alloc(), callInfo.thisArg(), types->clone(alloc().lifoAlloc()));
+                current->add(barrier);
+                callInfo.setThis(barrier);
+            }
         }
     }
 
     // Start inlining.
     LifoAlloc *lifoAlloc = alloc_->lifoAlloc();
     CompileInfo *info = lifoAlloc->new_<CompileInfo>(calleeScript, target,
                                                      (jsbytecode *)nullptr, callInfo.constructing(),
                                                      this->info().executionMode());
@@ -3821,16 +3813,17 @@ IonBuilder::inlineScriptedCall(CallInfo 
             IonSpew(IonSpew_Abort, "Inline builder raised exception.");
             abortReason_ = AbortReason_Error;
             return false;
         }
 
         // Inlining the callee failed. Mark the callee as uninlineable only if
         // the inlining was aborted for a non-exception reason.
         if (inlineBuilder.abortReason_ == AbortReason_Disable) {
+            AutoUnprotectCellForWrite unprotect(calleeScript);
             calleeScript->uninlineable = true;
             abortReason_ = AbortReason_Inlining;
         } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) {
             abortReason_ = AbortReason_Inlining;
         }
 
         return false;
     }
@@ -3926,17 +3919,17 @@ IonBuilder::patchInlinedReturns(CallInfo
 
     bottom->addPhi(phi);
     return phi;
 }
 
 static bool
 IsSmallFunction(JSScript *script)
 {
-    return script->length <= js_IonOptions.smallFunctionMaxBytecodeLength;
+    return script->getLength() <= js_IonOptions.smallFunctionMaxBytecodeLength;
 }
 
 bool
 IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
 {
     // Only inline when inlining is enabled.
     if (!inliningEnabled())
         return false;
@@ -3951,64 +3944,61 @@ IonBuilder::makeInliningDecision(JSFunct
 
     // Determine whether inlining is possible at callee site
     if (!canInlineTarget(target, callInfo))
         return false;
 
     // Heuristics!
     JSScript *targetScript = target->nonLazyScript();
 
+    IonSpew(IonSpew_Inlining, "Deciding whether to inline %s:%d",
+            targetScript->filename(), targetScript->getLineno());
+
     // Skip heuristics if we have an explicit hint to inline.
-    if (!targetScript->shouldInline) {
+    if (!targetScript->getShouldInline()) {
         // Cap the inlining depth.
         if (IsSmallFunction(targetScript)) {
             if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) {
-                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
-                        targetScript->filename(), targetScript->lineno);
+                IonSpew(IonSpew_Inlining, "Vetoed: exceeding allowed inline depth");
                 return false;
             }
         } else {
             if (inliningDepth_ >= js_IonOptions.maxInlineDepth) {
-                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
-                        targetScript->filename(), targetScript->lineno);
+                IonSpew(IonSpew_Inlining, "Vetoed: exceeding allowed inline depth");
                 return false;
             }
 
             if (targetScript->hasLoops()) {
-                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: big function that contains a loop",
-                        targetScript->filename(), targetScript->lineno);
+                IonSpew(IonSpew_Inlining, "Vetoed: big function that contains a loop");
                 return false;
             }
         }
 
         // Callee must not be excessively large.
         // This heuristic also applies to the callsite as a whole.
-        if (targetScript->length > js_IonOptions.inlineMaxTotalBytecodeLength) {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee excessively large.",
-                    targetScript->filename(), targetScript->lineno);
+        if (targetScript->getLength() > js_IonOptions.inlineMaxTotalBytecodeLength) {
+            IonSpew(IonSpew_Inlining, "Vetoed: callee excessively large");
             return false;
         }
 
         // Caller must be... somewhat hot. Ignore use counts when inlining for
         // the definite properties analysis, as the caller has not run yet.
         uint32_t callerUses = script()->getUseCount();
         if (callerUses < js_IonOptions.usesBeforeInlining() &&
             info().executionMode() != DefinitePropertiesAnalysis)
         {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: caller is insufficiently hot.",
-                    targetScript->filename(), targetScript->lineno);
+            IonSpew(IonSpew_Inlining, "Vetoed: caller is insufficiently hot");
             return false;
         }
 
         // Callee must be hot relative to the caller.
         if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses &&
             info().executionMode() != DefinitePropertiesAnalysis)
         {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
-                    targetScript->filename(), targetScript->lineno);
+            IonSpew(IonSpew_Inlining, "Vetoed: callee is not hot");
             return false;
         }
     }
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     targetType->watchStateChangeForInlinedCall(constraints());
 
@@ -4025,17 +4015,17 @@ IonBuilder::selectInliningTargets(Object
     if (!choiceSet.reserve(targets.length()))
         return false;
     for (size_t i = 0; i < targets.length(); i++) {
         JSFunction *target = &targets[i]->as<JSFunction>();
         bool inlineable = makeInliningDecision(target, callInfo);
 
         // Enforce a maximum inlined bytecode limit at the callsite.
         if (inlineable && target->isInterpreted()) {
-            totalSize += target->nonLazyScript()->length;
+            totalSize += target->nonLazyScript()->getLength();
             if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength)
                 inlineable = false;
         }
 
         choiceSet.append(inlineable);
         if (inlineable)
             numInlineable++;
     }
@@ -4526,16 +4516,17 @@ IonBuilder::inlineCalls(CallInfo &callIn
 }
 
 MInstruction *
 IonBuilder::createDeclEnvObject(MDefinition *callee, MDefinition *scope)
 {
     // Get a template CallObject that we'll use to generate inline object
     // creation.
     DeclEnvObject *templateObj = inspector->templateDeclEnvObject();
+    AutoUnprotectCell unprotect(templateObj);
 
     // One field is added to the function to handle its name.  This cannot be a
     // dynamic slot because there is still plenty of room on the DeclEnv object.
     JS_ASSERT(!templateObj->hasDynamicSlots());
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer.
@@ -4553,33 +4544,35 @@ IonBuilder::createDeclEnvObject(MDefinit
 }
 
 MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
     // Get a template CallObject that we'll use to generate inline object
     // creation.
     CallObject *templateObj = inspector->templateCallObject();
+    AutoUnprotectCell unprotect(templateObj);
 
     // If the CallObject needs dynamic slots, allocate those now.
     MInstruction *slots;
     if (templateObj->hasDynamicSlots()) {
-        size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(),
-                                                    templateObj->slotSpan());
+        AutoUnprotectCell unprotect(templateObj);
+        size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlotsForCompilation(),
+                                                    templateObj->lastProperty()->slotSpan(templateObj->getClass()));
         slots = MNewSlots::New(alloc(), nslots);
     } else {
         slots = MConstant::New(alloc(), NullValue());
     }
     current->add(slots);
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer. Run-once scripts need a singleton type, so always do a VM call
     // in such cases.
-    MInstruction *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce, slots);
+    MInstruction *callObj = MNewCallObject::New(alloc(), templateObj, script()->getTreatAsRunOnce(), slots);
     current->add(callObj);
 
     // Insert a post barrier to protect the following writes if we allocated
     // the new call object directly into tenured storage.
     if (templateObj->type()->isLongLivedForJITAlloc())
         current->add(MPostWriteBarrier::New(alloc(), callObj));
 
     // Initialize the object's reserved slots. No post barrier is needed here,
@@ -4587,18 +4580,18 @@ IonBuilder::createCallObject(MDefinition
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
 
     // Initialize argument slots.
     for (AliasedFormalIter i(script()); i; i++) {
         unsigned slot = i.scopeSlot();
         unsigned formal = i.frameIndex();
         MDefinition *param = current->getSlot(info().argSlotUnchecked(formal));
-        if (slot >= templateObj->numFixedSlots())
-            current->add(MStoreSlot::New(alloc(), slots, slot - templateObj->numFixedSlots(), param));
+        if (slot >= templateObj->numFixedSlotsForCompilation())
+            current->add(MStoreSlot::New(alloc(), slots, slot - templateObj->numFixedSlotsForCompilation(), param));
         else
             current->add(MStoreFixedSlot::New(alloc(), callObj, slot, param));
     }
 
     return callObj;
 }
 
 MDefinition *
@@ -4653,27 +4646,30 @@ IonBuilder::getSingletonPrototype(JSFunc
 MDefinition *
 IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee)
 {
     // Get the singleton prototype (if exists)
     JSObject *proto = getSingletonPrototype(target);
     if (!proto)
         return nullptr;
 
-    if (!target->nonLazyScript()->types)
-        return nullptr;
-
     JSObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject || !templateObject->is<JSObject>())
         return nullptr;
     if (templateObject->getProto() != proto)
         return nullptr;
 
-    if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject)))
-        return nullptr;
+    {
+        AutoUnprotectCellUnderCompilationLock unprotect(target->nonLazyScript());
+        if (!target->nonLazyScript()->types)
+            return nullptr;
+
+        if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject)))
+            return nullptr;
+    }
 
     // For template objects with NewScript info, the appropriate allocation
     // kind to use may change due to dynamic property adds. In these cases
     // calling Ion code will be invalidated, but any baseline template object
     // may be stale. Update to the correct template object in this case.
     types::TypeObject *templateType = templateObject->type();
     if (templateType->hasNewScript()) {
         templateObject = templateType->newScript()->templateObject;
@@ -4961,17 +4957,17 @@ IonBuilder::jsop_call(uint32_t argc, boo
     JS_ASSERT_IF(gotLambda, originals.length() <= 1);
 
     // If any call targets need to be cloned, look for existing clones to use.
     // Keep track of the originals as we need to case on them for poly inline.
     bool hasClones = false;
     ObjectVector targets(alloc());
     for (uint32_t i = 0; i < originals.length(); i++) {
         JSFunction *fun = &originals[i]->as<JSFunction>();
-        if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite) {
+        if (fun->hasScript() && fun->nonLazyScript()->getShouldCloneAtCallsite()) {
             if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), fun, script(), pc)) {
                 fun = clone;
                 hasClones = true;
             }
         }
         if (!targets.append(fun))
             return false;
     }
@@ -5096,27 +5092,30 @@ IonBuilder::testNeedsArgumentCheck(JSFun
 {
     // If we have a known target, check if the caller arg types are a subset of callee.
     // Since typeset accumulates and can't decrease that means we don't need to check
     // the arguments anymore.
     if (!target->hasScript())
         return true;
 
     JSScript *targetScript = target->nonLazyScript();
+
+    AutoUnprotectCellUnderCompilationLock lock(targetScript);
+
     if (!targetScript->types)
         return true;
 
     if (!ArgumentTypesMatch(callInfo.thisArg(), types::TypeScript::ThisTypes(targetScript)))
         return true;
-    uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->nargs);
+    uint32_t expected_args = Min<uint32_t>(callInfo.argc(), target->getNargs());
     for (size_t i = 0; i < expected_args; i++) {
         if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
             return true;
     }
-    for (size_t i = callInfo.argc(); i < target->nargs; i++) {
+    for (size_t i = callInfo.argc(); i < target->getNargs(); i++) {
         if (!types::TypeScript::ArgTypes(targetScript, i)->mightBeType(JSVAL_TYPE_UNDEFINED))
             return true;
     }
 
     return false;
 }
 
 MCall *
@@ -5127,17 +5126,17 @@ IonBuilder::makeCallHelper(JSFunction *t
     // This function may be called with mutated stack.
     // Querying TI for popped types is invalid.
 
     uint32_t targetArgs = callInfo.argc();
 
     // Collect number of missing arguments provided that the target is
     // scripted. Native functions are passed an explicit 'argc' parameter.
     if (target && !target->isNative())
-        targetArgs = Max<uint32_t>(target->nargs, callInfo.argc());
+        targetArgs = Max<uint32_t>(target->getNargs(), callInfo.argc());
 
     MCall *call =
         MCall::New(alloc(), target, targetArgs + 1, callInfo.argc(), callInfo.constructing());
     if (!call)
         return nullptr;
 
     // Explicitly pad any missing arguments with |undefined|.
     // This permits skipping the argumentsRectifier.
@@ -5313,19 +5312,20 @@ IonBuilder::jsop_eval(uint32_t argc)
 
         // Try to pattern match 'eval(v + "()")'. In this case v is likely a
         // name on the scope chain and the eval is performing a call on that
         // value. Use a dynamic scope chain lookup rather than a full eval.
         if (string->isConcat() &&
             string->getOperand(1)->isConstant() &&
             string->getOperand(1)->toConstant()->value().isString())
         {
-            JSString *str = string->getOperand(1)->toConstant()->value().toString();
-
-            if (str->isLinear() && StringEqualsAscii(&str->asLinear(), "()")) {
+            JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom();
+            AutoUnprotectCell unprotect(atom);
+
+            if (StringEqualsAscii(atom, "()")) {
                 MDefinition *name = string->getOperand(0);
                 MInstruction *dynamicName = MGetDynamicName::New(alloc(), scopeChain, name);
                 current->add(dynamicName);
 
                 MInstruction *thisv = MPassArg::New(alloc(), thisValue);
                 current->add(thisv);
 
                 current->push(dynamicName);
@@ -5368,17 +5368,17 @@ IonBuilder::jsop_compare(JSOp op)
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
-    JS_ASSERT(script()->compileAndGo);
+    JS_ASSERT(script()->getCompileAndGo());
 
     JSObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject)
         return abort("No template object for NEWARRAY");
 
     JS_ASSERT(templateObject->is<ArrayObject>());
     if (templateObject->type()->unknownProperties()) {
         // We will get confused in jsop_initelem_array if we can't find the
@@ -5398,17 +5398,17 @@ IonBuilder::jsop_newarray(uint32_t count
 
     return true;
 }
 
 bool
 IonBuilder::jsop_newobject()
 {
     // Don't bake in the TypeObject for non-CNG scripts.
-    JS_ASSERT(script()->compileAndGo);
+    JS_ASSERT(script()->getCompileAndGo());
 
     JSObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject)
         return abort("No template object for NEWOBJECT");
 
     JS_ASSERT(templateObject->is<JSObject>());
     MNewObject *ins = MNewObject::New(alloc(), templateObject,
                                       /* templateObjectIsClassPrototype = */ false);
@@ -5465,17 +5465,20 @@ IonBuilder::jsop_initelem_array()
 
     MConstant *id = MConstant::New(alloc(), Int32Value(GET_UINT24(pc)));
     current->add(id);
 
     // Get the elements vector.
     MElements *elements = MElements::New(alloc(), obj);
     current->add(elements);
 
-    if (obj->toNewArray()->templateObject()->shouldConvertDoubleElements()) {
+    JSObject *templateObject = obj->toNewArray()->templateObject();
+    AutoUnprotectCell unprotect(templateObject);
+
+    if (templateObject->shouldConvertDoubleElementsForCompilation()) {
         MInstruction *valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
 
     // Store the value.
     MStoreElement *store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false);
     current->add(store);
@@ -5492,18 +5495,21 @@ IonBuilder::jsop_initelem_array()
 
 bool
 IonBuilder::jsop_initprop(PropertyName *name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     JSObject *templateObject = obj->toNewObject()->templateObject();
-
-    Shape *shape = templateObject->nativeLookupPure(name);
+    Shape *shape;
+    {
+        AutoUnprotectCell unprotect(templateObject);
+        shape = templateObject->lastProperty()->searchLinear(NameToId(name));
+    }
 
     if (!shape) {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(alloc(), obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
@@ -5665,17 +5671,17 @@ IonBuilder::newOsrPreheader(MBasicBlock 
         }
 
         osrBlock->add(scopev);
         osrBlock->initSlot(slot, scopev);
     }
     // Initialize |return value|
     {
         MInstruction *returnValue;
-        if (!script()->noScriptRval)
+        if (!script()->getNoScriptRval())
             returnValue = MOsrReturnValue::New(alloc(), entry);
         else
             returnValue = MConstant::New(alloc(), UndefinedValue());
         osrBlock->add(returnValue);
         osrBlock->initSlot(info().returnValueSlot(), returnValue);
     }
 
     // Initialize arguments object.
@@ -5833,20 +5839,24 @@ IonBuilder::newPendingLoopHeader(MBasicB
 
             // Get the value from the baseline frame.
             Value existingValue;
             uint32_t arg = i - info().firstArgSlot();
             uint32_t var = i - info().firstLocalSlot();
             if (info().fun() && i == info().thisSlot()) {
                 existingValue = baselineFrame_->thisValue();
             } else if (arg < info().nargs()) {
-                if (info().needsArgsObj())
+                if (info().needsArgsObj()) {
+                    // Note: baseline frame contents need to be copied into temporary space
+                    // when triggering an OSR off thread compilation.
+                    AutoUnprotectCell unprotect(&baselineFrame_->argsObj());
                     existingValue = baselineFrame_->argsObj().arg(arg);
-                else
+                } else {
                     existingValue = baselineFrame_->unaliasedFormal(arg);
+                }
             } else {
                 existingValue = baselineFrame_->unaliasedVar(var);
             }
 
             // Extract typeset from value.
             MIRType type = existingValue.isDouble()
                          ? MIRType_Double
                          : MIRTypeFromValueType(existingValue.extractNonDoubleType());
@@ -6301,19 +6311,16 @@ bool
 IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name)
 {
     jsid id = NameToId(name);
 
     JS_ASSERT(staticObject->is<GlobalObject>() || staticObject->is<CallObject>());
 
     MDefinition *value = current->peek(-1);
 
-    if (staticObject->watched())
-        return jsop_setprop(name);
-
     types::TypeObjectKey *staticType = types::TypeObjectKey::get(staticObject);
     if (staticType->unknownProperties())
         return jsop_setprop(name);
 
     types::HeapTypeSetKey property = staticType->property(id);
     if (!property.maybeTypes() ||
         !property.maybeTypes()->definiteProperty() ||
         property.configured(constraints(), staticType))
@@ -6704,17 +6711,17 @@ IonBuilder::getElemTryTypedStatic(bool *
     if (!obj->resultTypeSet())
         return true;
 
     JSObject *tarrObj = obj->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
     TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
+    ArrayBufferView::ViewType viewType = (ArrayBufferView::ViewType) tarr->type();
 
     // LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
     if (viewType == ArrayBufferView::TYPE_UINT32)
         return true;
 
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
@@ -7261,17 +7268,17 @@ IonBuilder::setElemTryTypedStatic(bool *
 
     if (!object->resultTypeSet())
         return true;
     JSObject *tarrObj = object->resultTypeSet()->getSingleton();
     if (!tarrObj)
         return true;
 
     TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
-    ArrayBufferView::ViewType viewType = JS_GetArrayBufferViewType(tarr);
+    ArrayBufferView::ViewType viewType = (ArrayBufferView::ViewType) tarr->type();
 
     MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
     if (!ptr)
         return true;
 
     // Emit StoreTypedArrayElementStatic.
     object->setFoldedUnchecked();
     index->setFoldedUnchecked();
@@ -7394,17 +7401,17 @@ IonBuilder::setElemTryCache(bool *emitte
     // chain, we know that we anen't missing any setters by overwriting the hole with
     // another value.
     bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), object, value));
 
     // Emit SetElementCache.
-    MInstruction *ins = MSetElementCache::New(alloc(), object, index, value, script()->strict, guardHoles);
+    MInstruction *ins = MSetElementCache::New(alloc(), object, index, value, script()->getStrict(), guardHoles);
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
         return false;
 
     *emitted = true;
     return true;
@@ -7965,17 +7972,17 @@ IonBuilder::annotateGetPropertyCache(MDe
 
 // Returns true if an idempotent cache has ever invalidated this script
 // or an outer script.
 bool
 IonBuilder::invalidatedIdempotentCache()
 {
     IonBuilder *builder = this;
     do {
-        if (builder->script()->invalidatedIdempotentCache)
+        if (builder->script()->getInvalidatedIdempotentCache())
             return true;
         builder = builder->callerBuilder_;
     } while (builder);
 
     return false;
 }
 
 bool
@@ -8001,19 +8008,16 @@ IonBuilder::loadSlot(MDefinition *obj, s
     load->setResultType(rvalType);
     return pushTypeBarrier(load, types, barrier);
 }
 
 bool
 IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType,
                      bool barrier, types::TemporaryTypeSet *types)
 {
-    JS_ASSERT(shape->hasDefaultGetter());
-    JS_ASSERT(shape->hasSlot());
-
     return loadSlot(obj, shape->slot(), shape->numFixedSlots(), rvalType, barrier, types);
 }
 
 bool
 IonBuilder::storeSlot(MDefinition *obj, size_t slot, size_t nfixed,
                       MDefinition *value, bool needsBarrier,
                       MIRType slotType /* = MIRType_None */)
 {
@@ -8038,20 +8042,17 @@ IonBuilder::storeSlot(MDefinition *obj, 
         store->setSlotType(slotType);
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
                       MIRType slotType /* = MIRType_None */)
 {
-    JS_ASSERT(shape->hasDefaultSetter());
     JS_ASSERT(shape->writable());
-    JS_ASSERT(shape->hasSlot());
-
     return storeSlot(obj, shape->slot(), shape->numFixedSlots(), value, needsBarrier, slotType);
 }
 
 bool
 IonBuilder::jsop_getprop(PropertyName *name)
 {
     bool emitted = false;
 
@@ -8549,17 +8550,17 @@ IonBuilder::jsop_setprop(PropertyName *n
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
     bool emitted = false;
 
     // Always use a call if we are doing the definite properties analysis and
     // not actually emitting code, to simplify later analysis.
     if (info().executionMode() == DefinitePropertiesAnalysis) {
-        MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->strict);
+        MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->getStrict());
         current->add(ins);
         current->push(value);
         return resumeAfter(ins);
     }
 
     // Add post barrier if needed.
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
@@ -8584,17 +8585,17 @@ IonBuilder::jsop_setprop(PropertyName *n
     if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Try to emit a polymorphic cache.
     if (!setPropTryCache(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Emit call.
-    MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->strict);
+    MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->getStrict());
     current->add(ins);
     current->push(value);
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj,
                                    PropertyName *name, MDefinition *value)
@@ -8858,17 +8859,17 @@ IonBuilder::setPropTryInlineAccess(bool 
 bool
 IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj,
                             PropertyName *name, MDefinition *value,
                             bool barrier, types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
     // Emit SetPropertyCache.
-    MSetPropertyCache *ins = MSetPropertyCache::New(alloc(), obj, value, name, script()->strict, barrier);
+    MSetPropertyCache *ins = MSetPropertyCache::New(alloc(), obj, value, name, script()->getStrict(), barrier);
 
     if (!objTypes || objTypes->propertyNeedsBarrier(constraints(), NameToId(name)))
         ins->setNeedsBarrier();
 
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
@@ -8902,43 +8903,43 @@ IonBuilder::jsop_delelem()
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_regexp(RegExpObject *reobj)
 {
-    JSObject *prototype = reobj->getProto();
-    JS_ASSERT(prototype == script()->global().maybeGetRegExpPrototype());
-
-    JS_ASSERT(&reobj->JSObject::global() == &script()->global());
-
     // JS semantics require regular expression literals to create different
     // objects every time they execute. We only need to do this cloning if the
     // script could actually observe the effect of such cloning, for instance
     // by getting or setting properties on it.
     //
     // First, make sure the regex is one we can safely optimize. Lowering can
     // then check if this regex object only flows into known natives and can
     // avoid cloning in this case.
 
+    // RegExpObjects embedded in scripts are immutable.
+    AutoUnprotectCell unprotect(reobj);
+
     bool mustClone = true;
     types::TypeObjectKey *typeObj = types::TypeObjectKey::get(&script()->global());
     if (!typeObj->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
         RegExpStatics *res = script()->global().getRegExpStatics();
 
         DebugOnly<uint32_t> origFlags = reobj->getFlags();
         DebugOnly<uint32_t> staticsFlags = res->getFlags();
         JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
 
         if (!reobj->global() && !reobj->sticky())
             mustClone = false;
     }
 
+    JSObject *prototype = reobj->getProto();
+
     MRegExp *regexp = MRegExp::New(alloc(), reobj, prototype, mustClone);
     current->add(regexp);
     current->push(regexp);
 
     regexp->setMovable();
 
     // The MRegExp is set to be movable.
     // That would be incorrect for global/sticky, because lastIndex could be wrong.
@@ -9088,17 +9089,17 @@ IonBuilder::jsop_deffun(uint32_t index)
 }
 
 bool
 IonBuilder::jsop_this()
 {
     if (!info().fun())
         return abort("JSOP_THIS outside of a JSFunction.");
 
-    if (script()->strict || info().fun()->isSelfHostedBuiltin()) {
+    if (script()->getStrict() || info().fun()->isSelfHostedBuiltin()) {
         // No need to wrap primitive |this| in strict mode or self-hosted code.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     if (thisTypes->getKnownTypeTag() == JSVAL_TYPE_OBJECT ||
         (thisTypes->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject()))
     {
@@ -9241,17 +9242,17 @@ IonBuilder::walkScopeChain(unsigned hops
 
     return scope;
 }
 
 bool
 IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall)
 {
     JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc);
-    if (!outerScript || !outerScript->treatAsRunOnce)
+    if (!outerScript || !outerScript->getTreatAsRunOnce())
         return false;
 
     types::TypeObjectKey *funType = types::TypeObjectKey::get(outerScript->function());
     if (funType->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED))
         return false;
 
     // The script this aliased var operation is accessing will run only once,
     // so there will be only one call object and the aliased var access can be
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -83,16 +83,17 @@ class IonCode : public gc::BarrieredCell
         return jumpRelocTableOffset() + jumpRelocTableBytes_;
     }
     uint32_t preBarrierTableOffset() const {
         return dataRelocTableOffset() + dataRelocTableBytes_;
     }
 
   public:
     uint8_t *raw() const {
+        AutoUnprotectCell unprotect(this);
         return code_;
     }
     size_t instructionsSize() const {
         return insnSize_;
     }
     void trace(JSTracer *trc);
     void finalize(FreeOp *fop);
     void setInvalidated() {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -680,17 +680,16 @@ MacroAssembler::newGCThing(const Registe
     subPtr(Imm32(thingSize), result);
 }
 
 void
 MacroAssembler::newGCThing(const Register &result, JSObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
-    JS_ASSERT(!templateObject->hasDynamicElements());
 
     gc::InitialHeap initialHeap = templateObject->type()->initialHeapForJITAlloc();
     newGCThing(result, allocKind, fail, initialHeap);
 }
 
 void
 MacroAssembler::newGCString(const Register &result, Label *fail)
 {
@@ -753,17 +752,16 @@ MacroAssembler::newGCThingPar(const Regi
 
 void
 MacroAssembler::newGCThingPar(const Register &result, const Register &slice,
                               const Register &tempReg1, const Register &tempReg2,
                               JSObject *templateObject, Label *fail)
 {
     gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
     JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
-    JS_ASSERT(!templateObject->hasDynamicElements());
 
     newGCThingPar(result, slice, tempReg1, tempReg2, allocKind, fail);
 }
 
 void
 MacroAssembler::newGCStringPar(const Register &result, const Register &slice,
                                const Register &tempReg1, const Register &tempReg2,
                                Label *fail)
@@ -779,46 +777,52 @@ MacroAssembler::newGCShortStringPar(cons
     newGCThingPar(result, slice, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
 }
 
 void
 MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject)
 {
     // Fast initialization of an empty object returned by NewGCThing().
 
+    // Template objects provided to the code generator should not be modified by the main thread.
+    AutoUnprotectCell unprotect(templateObject);
+
+    JS_ASSERT(!templateObject->hasDynamicElements());
+
     storePtr(ImmGCPtr(templateObject->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObject->type()), Address(obj, JSObject::offsetOfType()));
     storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots()));
 
     if (templateObject->is<ArrayObject>()) {
-        JS_ASSERT(!templateObject->getDenseInitializedLength());
+        JS_ASSERT(!templateObject->getDenseInitializedLengthForCompilation());
 
         int elementsOffset = JSObject::offsetOfFixedElements();
 
         addPtr(Imm32(elementsOffset), obj);
         storePtr(obj, Address(obj, -elementsOffset + JSObject::offsetOfElements()));
         addPtr(Imm32(-elementsOffset), obj);
 
         // Fill in the elements header.
-        store32(Imm32(templateObject->getDenseCapacity()),
+        store32(Imm32(templateObject->getDenseCapacityForCompilation()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfCapacity()));
-        store32(Imm32(templateObject->getDenseInitializedLength()),
+        store32(Imm32(templateObject->getDenseInitializedLengthForCompilation()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfInitializedLength()));
         store32(Imm32(templateObject->as<ArrayObject>().length()),
                 Address(obj, elementsOffset + ObjectElements::offsetOfLength()));
-        store32(Imm32(templateObject->shouldConvertDoubleElements()
+        store32(Imm32(templateObject->shouldConvertDoubleElementsForCompilation()
                       ? ObjectElements::CONVERT_DOUBLE_ELEMENTS
                       : 0),
                 Address(obj, elementsOffset + ObjectElements::offsetOfFlags()));
     } else {
         storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements()));
 
         // Fixed slots of non-array objects are required to be initialized.
         // Use the values currently in the template object.
-        size_t nslots = Min(templateObject->numFixedSlots(), templateObject->slotSpan());
+        size_t nslots = Min(templateObject->numFixedSlotsForCompilation(),
+                            templateObject->lastProperty()->slotSpan(templateObject->getClassImmutable()));
         for (unsigned i = 0; i < nslots; i++) {
             storeValue(templateObject->getFixedSlot(i),
                        Address(obj, JSObject::getFixedSlotOffset(i)));
         }
     }
 
     if (templateObject->hasPrivate()) {
         uint32_t nfixed = templateObject->numFixedSlots();
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -243,18 +243,20 @@ IonBuilder::inlineArray(CallInfo &callIn
         if (initLength >= JSObject::NELEMENTS_LIMIT)
             return InliningStatus_NotInlined;
     }
 
     callInfo.unwrapArgs();
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         getInlineReturnTypeSet()->convertDoubleElements(constraints());
-    if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
+    if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) {
+        AutoUnprotectCell unprotect(templateObject);
         templateObject->setShouldConvertDoubleElements();
+    }
 
     MNewArray *ins = MNewArray::New(alloc(), initLength, templateObject, allocating);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
         // Get the elements vector.
         MElements *elements = MElements::New(alloc(), ins);
@@ -452,21 +454,18 @@ IonBuilder::inlineArrayConcat(CallInfo &
     // global, so we can create the result object inline.
     if (thisTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
 
     types::TypeObject *baseThisType = thisTypes->getTypeObject(0);
     if (!baseThisType)
         return InliningStatus_NotInlined;
     types::TypeObjectKey *thisType = types::TypeObjectKey::get(baseThisType);
-    if (thisType->unknownProperties() ||
-        &thisType->proto().toObject()->global() != &script()->global())
-    {
+    if (thisType->unknownProperties())
         return InliningStatus_NotInlined;
-    }
 
     // Don't inline if 'this' is packed and the argument may not be packed
     // (the result array will reuse the 'this' type).
     if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) &&
         argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED))
     {
         return InliningStatus_NotInlined;
     }
@@ -1260,17 +1259,17 @@ IonBuilder::inlineNewParallelArray(CallI
     if (argc < 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     types::TemporaryTypeSet *ctorTypes = callInfo.getArg(0)->resultTypeSet();
     JSObject *targetObj = ctorTypes ? ctorTypes->getSingleton() : nullptr;
     JSFunction *target = nullptr;
     if (targetObj && targetObj->is<JSFunction>())
         target = &targetObj->as<JSFunction>();
-    if (target && target->isInterpreted() && target->nonLazyScript()->shouldCloneAtCallsite) {
+    if (target && target->isInterpreted() && target->nonLazyScript()->getShouldCloneAtCallsite()) {
         if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc))
             target = clone;
     }
     MDefinition *ctor = makeCallsiteClone(
         target,
         callInfo.getArg(0)->toPassArg()->getArgument());
 
     // Discard the function.
@@ -1285,17 +1284,17 @@ IonBuilder::inlineParallelArray(CallInfo
     if (!callInfo.constructing())
         return InliningStatus_NotInlined;
 
     uint32_t argc = callInfo.argc();
     JSFunction *target = ParallelArrayObject::maybeGetConstructor(&script()->global(), argc);
     if (!target)
         return InliningStatus_NotInlined;
 
-    JS_ASSERT(target->nonLazyScript()->shouldCloneAtCallsite);
+    JS_ASSERT(target->nonLazyScript()->getShouldCloneAtCallsite());
     if (JSFunction *clone = ExistingCloneFunctionAtCallsite(compartment->callsiteClones(), target, script(), pc))
         target = clone;
 
     MConstant *ctor = MConstant::New(alloc(), ObjectValue(*target));
     current->add(ctor);
 
     return inlineParallelArrayTail(callInfo, target, ctor, nullptr, 0,
                                    ParallelArrayObject::construct);
@@ -1319,27 +1318,27 @@ IonBuilder::inlineParallelArrayTail(Call
     // constructed type objects, so we can only perform the inlining if we
     // already have one of these type objects.
     types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet();
     if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
         return InliningStatus_NotInlined;
     if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
-    if (!typeObject || typeObject->clasp != &ParallelArrayObject::class_)
+    if (!typeObject || typeObject->getClass() != &ParallelArrayObject::class_)
         return InliningStatus_NotInlined;
 
     JSObject *templateObject = inspector->getTemplateObjectForNative(pc, native);
     if (!templateObject || templateObject->type() != typeObject)
         return InliningStatus_NotInlined;
 
     // Create the call and add in the non-this arguments.
     uint32_t targetArgs = argc;
     if (target && !target->isNative())
-        targetArgs = Max<uint32_t>(target->nargs, argc);
+        targetArgs = Max<uint32_t>(target->getNargs(), argc);
 
     MCall *call = MCall::New(alloc(), target, targetArgs + 1, argc, false);
     if (!call)
         return InliningStatus_Error;
 
     callInfo.unwrapArgs();
 
     // Explicitly pad any missing arguments with |undefined|.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -656,16 +656,17 @@ MApplyArgs::New(TempAllocator &alloc, JS
     return new(alloc) MApplyArgs(target, fun, argc, self);
 }
 
 MDefinition*
 MStringLength::foldsTo(TempAllocator &alloc, bool useValueNumbers)
 {
     if ((type() == MIRType_Int32) && (string()->isConstant())) {
         Value value = string()->toConstant()->value();
+        AutoUnprotectCell unprotect(&value.toString()->asAtom());
         size_t length = JS_GetStringLength(value.toString());
 
         return MConstant::New(alloc, Int32Value(length));
     }
 
     return this;
 }
 
@@ -2314,20 +2315,18 @@ MCompare::evaluateConstantOperands(bool 
         return false;
 
     Value lhs = left->toConstant()->value();
     Value rhs = right->toConstant()->value();
 
     // Fold away some String equality comparisons.
     if (lhs.isString() && rhs.isString()) {
         int32_t comp = 0; // Default to equal.
-        if (left != right) {
-            if (!CompareStrings(GetIonContext()->cx, lhs.toString(), rhs.toString(), &comp))
-                return false;
-        }
+        if (left != right)
+            comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom());
         
         switch (jsop_) {
           case JSOP_LT:
             *result = (comp < 0);
             break;
           case JSOP_LE:
             *result = (comp <= 0);
             break;
@@ -2498,16 +2497,17 @@ MBeta::printOpcode(FILE *fp) const
     sp.init();
     comparison_->print(sp);
     fprintf(fp, " %s", sp.string());
 }
 
 bool
 MNewObject::shouldUseVM() const
 {
+    AutoUnprotectCell unprotect(templateObject());
     return templateObject()->hasSingletonType() ||
            templateObject()->hasDynamicSlots();
 }
 
 bool
 MNewArray::shouldUseVM() const
 {
     JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT);
@@ -2923,17 +2923,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
                                   types::TemporaryTypeSet *observed, bool updateObserved)
 {
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
     if (updateObserved && observed->empty() && name) {
         JSObject *obj = object->singleton() ? object->singleton() : object->proto().toObjectOrNull();
 
         while (obj) {
-            if (!obj->isNative())
+            if (!obj->getClass()->isNative())
                 break;
 
             types::TypeObjectKey *typeObj = types::TypeObjectKey::get(obj);
             if (propertycx)
                 typeObj->ensureTrackedProperty(propertycx, NameToId(name));
 
             if (!typeObj->unknownProperties()) {
                 types::HeapTypeSetKey property = typeObj->property(NameToId(name));
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -961,18 +961,28 @@ class MConstant : public MNullaryInstruc
 
     const js::Value &value() const {
         return value_;
     }
     const js::Value *vp() const {
         return &value_;
     }
     const bool valueToBoolean() const {
-        // A hack to avoid this wordy pattern everywhere in the JIT.
-        return ToBoolean(HandleValue::fromMarkedLocation(&value_));
+        if (value_.isString()) {
+            AutoUnprotectCell unprotect(&value_.toString()->asAtom());
+            return ToBoolean(value_);
+        }
+        if (value_.isObject()) {
+            // Note: ToBoolean can't be called off thread as it will try to
+            // unwrap wrappers. IsWrapper goes through public API methods which
+            // don't unprotect the right pointers. Since wrappers don't have
+            // singleton type just ignore this case.
+            return !value_.toObject().getClass()->emulatesUndefined();
+        }
+        return ToBoolean(value_);
     }
 
     void printOpcode(FILE *fp) const;
 
     HashNumber valueHash() const;
     bool congruentTo(MDefinition *ins) const;
 
     AliasSet getAliasSet() const {
@@ -4811,32 +4821,33 @@ class MRegExpTest
 };
 
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
     // the script, and are immutable except for delazification. Record this
     // information while still on the main thread to avoid races.
     CompilerRootFunction fun;
+    uint16_t nargs;
     uint16_t flags;
     gc::Cell *scriptOrLazyScript;
     bool singletonType;
     bool useNewTypeForClone;
 
     LambdaFunctionInfo(JSFunction *fun)
-      : fun(fun), flags(fun->flags),
+      : fun(fun), nargs(fun->getNargs()), flags(fun->getFlags()),
         scriptOrLazyScript(fun->hasScript()
                            ? (gc::Cell *) fun->nonLazyScript()
                            : (gc::Cell *) fun->lazyScript()),
         singletonType(fun->hasSingletonType()),
         useNewTypeForClone(types::UseNewTypeForClone(fun))
     {}
 
     LambdaFunctionInfo(const LambdaFunctionInfo &info)
-      : fun((JSFunction *) info.fun), flags(info.flags),
+      : fun((JSFunction *) info.fun), nargs(info.nargs), flags(info.flags),
         scriptOrLazyScript(info.scriptOrLazyScript),
         singletonType(info.singletonType),
         useNewTypeForClone(info.useNewTypeForClone)
     {}
 };
 
 class MLambda
   : public MUnaryInstruction,
@@ -5954,17 +5965,19 @@ class MLoadTypedArrayElementStatic
     INSTRUCTION_HEADER(LoadTypedArrayElementStatic);
 
     static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray,
                                              MDefinition *ptr)
     {
         return new(alloc) MLoadTypedArrayElementStatic(typedArray, ptr);
     }
 
-    ArrayBufferView::ViewType viewType() const { return JS_GetArrayBufferViewType(typedArray_); }
+    ArrayBufferView::ViewType viewType() const {
+        return (ArrayBufferView::ViewType) typedArray_->type();
+    }
     void *base() const;
     size_t length() const;
 
     MDefinition *ptr() const { return getOperand(0); }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
@@ -6136,17 +6149,19 @@ class MStoreTypedArrayElementStatic :
     {
         return new(alloc) MStoreTypedArrayElementStatic(typedArray, ptr, v);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
-    ArrayBufferView::ViewType viewType() const { return JS_GetArrayBufferViewType(typedArray_); }
+    ArrayBufferView::ViewType viewType() const {
+        return (ArrayBufferView::ViewType) typedArray_->type();
+    }
     bool isFloatArray() const {
         return (viewType() == ArrayBufferView::TYPE_FLOAT32 ||
                 viewType() == ArrayBufferView::TYPE_FLOAT64);
     }
 
     void *base() const;
     size_t length() const;
 
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -463,17 +463,17 @@ class MBasicBlock : public TempObject, p
     void setLoopDepth(uint32_t loopDepth) {
         loopDepth_ = loopDepth;
     }
     uint32_t loopDepth() const {
         return loopDepth_;
     }
 
     bool strict() const {
-        return info_.script()->strict;
+        return info_.script()->getStrict();
     }
 
     void dumpStack(FILE *fp);
 
     void dump(FILE *fp);
 
     // Track bailouts by storing the current pc in MIR instruction added at this
     // cycle. This is also used for tracking calls when profiling.
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -253,19 +253,19 @@ do {                                    
 static bool
 CompareStringsPar(ForkJoinSlice *slice, JSString *left, JSString *right, int32_t *res)
 {
     ScopedThreadSafeStringInspector leftInspector(left);
     ScopedThreadSafeStringInspector rightInspector(right);
     if (!leftInspector.ensureChars(slice) || !rightInspector.ensureChars(slice))
         return false;
 
-    return CompareChars(leftInspector.chars(), left->length(),
-                        rightInspector.chars(), right->length(),
-                        res);
+    *res = CompareChars(leftInspector.chars(), left->length(),
+                        rightInspector.chars(), right->length());
+    return true;
 }
 
 static bool
 CompareMaybeStringsPar(ForkJoinSlice *slice, HandleValue v1, HandleValue v2, int32_t *res)
 {
     if (!v1.isString())
         return false;
     if (!v2.isString())
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -301,28 +301,28 @@ SnapshotWriter::startSnapshot(uint32_t f
 void
 SnapshotWriter::startFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack)
 {
     // Test if we honor the maximum of arguments at all times.
     // This is a sanity check and not an algorithm limit. So check might be a bit too loose.
     // +4 to account for scope chain, return value, this value and maybe arguments_object.
     JS_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
 
-    uint32_t implicit = StartArgSlot(script, fun);
+    uint32_t implicit = StartArgSlot(script);
     uint32_t formalArgs = CountArgSlots(script, fun);
 
-    nslots_ = formalArgs + script->nfixed + exprStack;
+    nslots_ = formalArgs + script->getNfixed() + exprStack;
     slotsWritten_ = 0;
 
     IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
-            implicit, formalArgs - implicit, script->nfixed, exprStack);
+            implicit, formalArgs - implicit, script->getNfixed(), exprStack);
 
-    JS_ASSERT(script->code <= pc && pc <= script->code + script->length);
+    JS_ASSERT(script->getCode() <= pc && pc <= script->getCode() + script->getLength());
 
-    uint32_t pcoff = uint32_t(pc - script->code);
+    uint32_t pcoff = uint32_t(pc - script->getCode());
     IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nslots_);
     writer_.writeUnsigned(pcoff);
     writer_.writeUnsigned(nslots_);
 }
 
 #ifdef TRACK_SNAPSHOTS
 void
 SnapshotWriter::trackFrame(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId,
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -274,16 +274,17 @@ CodeGeneratorShared::encode(LSnapshot *s
         // Ensure that all snapshot which are encoded can safely be used for
         // bailouts.
         DebugOnly<jsbytecode *> bailPC = pc;
         if (mir->mode() == MResumePoint::ResumeAfter)
           bailPC = GetNextPc(pc);
 
 #ifdef DEBUG
         if (GetIonContext()->cx) {
+            AutoUnprotectCell unprotect(script);
             uint32_t stackDepth;
             if (ReconstructStackDepth(GetIonContext()->cx, script, bailPC, &stackDepth)) {
                 if (JSOp(*bailPC) == JSOP_FUNCALL) {
                     // For fun.call(this, ...); the reconstructStackDepth will
                     // include the this. When inlining that is not included.
                     // So the exprStackSlots will be one less.
                     JS_ASSERT(stackDepth - exprStack <= 1);
                 } else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -220,17 +220,17 @@ FollowBranch(JSContext *cx, JSScript *sc
 /* Common representation of slots throughout analyses and the compiler. */
 static inline uint32_t ThisSlot() {
     return 0;
 }
 static inline uint32_t ArgSlot(uint32_t arg) {
     return 1 + arg;
 }
 static inline uint32_t LocalSlot(JSScript *script, uint32_t local) {
-    return 1 + (script->function() ? script->function()->nargs : 0) + local;
+    return 1 + (script->function() ? script->function()->getNargs() : 0) + local;
 }
 static inline uint32_t TotalSlots(JSScript *script) {
     return LocalSlot(script, 0) + script->nfixed;
 }
 
 static inline uint32_t StackSlot(JSScript *script, uint32_t index) {
     return TotalSlots(script) + index;
 }
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -55,17 +55,16 @@ BEGIN_TEST(testConservativeGC)
 
     return true;
 }
 
 bool checkObjectFields(JSObject *savedCopy, JSObject *obj)
 {
     /* Ignore fields which are unstable across GCs. */
     CHECK(savedCopy->lastProperty() == obj->lastProperty());
-    CHECK(savedCopy->getProto() == obj->getProto());
     return true;
 }
 
 END_TEST(testConservativeGC)
 
 BEGIN_TEST(testDerivedValues)
 {
   JSString *str = JS_NewStringCopyZ(cx, "once upon a midnight dreary");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1051,17 +1051,17 @@ namespace js {
  */
 extern JS_PUBLIC_API(bool)
 ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
 
 /*
  * DO NOT CALL THIS. Use JS::ToBoolean
  */
 extern JS_PUBLIC_API(bool)
-ToBooleanSlow(JS::HandleValue v);
+ToBooleanSlow(const JS::Value &v);
 
 /*
  * DO NOT CALL THIS. Use JS::ToString
  */
 extern JS_PUBLIC_API(JSString*)
 ToStringSlow(JSContext *cx, JS::HandleValue v);
 } /* namespace js */
 
@@ -1080,17 +1080,17 @@ ToNumber(JSContext *cx, Handle<Value> v,
     if (v.isNumber()) {
         *out = v.toNumber();
         return true;
     }
     return js::ToNumberSlow(cx, v, out);
 }
 
 JS_ALWAYS_INLINE bool
-ToBoolean(HandleValue v)
+ToBoolean(const Value &v)
 {
     if (v.isBoolean())
         return v.toBoolean();
     if (v.isInt32())
         return v.toInt32() != 0;
     if (v.isNullOrUndefined())
         return false;
     if (v.isDouble()) {
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1486,20 +1486,20 @@ CompareLexicographicInt32(JSContext *cx,
 
 static inline bool
 CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
                        const jschar *s2, size_t l2, bool *lessOrEqualp)
 {
     if (!JS_CHECK_OPERATION_LIMIT(cx))
         return false;
 
-    int32_t result;
-    if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result))
+    if (!s1 || !s2)
         return false;
 
+    int32_t result = CompareChars(s1, l1, s2, l2);
     *lessOrEqualp = (result <= 0);
     return true;
 }
 
 struct SortComparatorStrings
 {
     JSContext   *const cx;
 
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -26,16 +26,17 @@ js::AtomStateEntry::asPtr() const
     return atom;
 }
 
 namespace js {
 
 inline jsid
 AtomToId(JSAtom *atom)
 {
+    AutoUnprotectCell unprotect(atom);
     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
 
     uint32_t index;
     if (atom->isIndex(&index) && index <= JSID_INT_MAX)
         return INT_TO_JSID(int32_t(index));
 
     return JSID_FROM_BITS(size_t(atom));
 }
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -177,17 +177,17 @@ js_InitBooleanClass(JSContext *cx, Handl
 
 JSString *
 js_BooleanToString(ExclusiveContext *cx, bool b)
 {
     return b ? cx->names().true_ : cx->names().false_;
 }
 
 JS_PUBLIC_API(bool)
-js::ToBooleanSlow(HandleValue v)
+js::ToBooleanSlow(const Value &v)
 {
     if (v.isString())
         return v.toString()->length() != 0;
 
     JS_ASSERT(v.isObject());
     return !EmulatesUndefined(&v.toObject());
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -108,30 +108,30 @@ JSCompartment::sweepCallsiteClones()
         }
     }
 }
 
 JSFunction *
 js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun,
                                     JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite);
+    JS_ASSERT(fun->nonLazyScript()->getShouldCloneAtCallsite());
     JS_ASSERT(!fun->nonLazyScript()->enclosingStaticScope());
     JS_ASSERT(types::UseNewTypeForClone(fun));
 
     /*
      * If we start allocating function objects in the nursery, then the callsite
      * clone table will need a postbarrier.
      */
     JS_ASSERT(fun->isTenured());
 
     if (!table.initialized())
         return nullptr;
 
-    CallsiteCloneTable::Ptr p = table.lookup(CallsiteCloneKey(fun, script, pc - script->code));
+    CallsiteCloneTable::Ptr p = table.lookup(CallsiteCloneKey(fun, script, pc - script->getCode()));
     if (p)
         return p->value;
 
     return nullptr;
 }
 
 JSFunction *
 js::CloneFunctionAtCallsite(JSContext *cx, HandleFunction fun, HandleScript script, jsbytecode *pc)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -46,17 +46,17 @@ struct CallsiteCloneKey {
     /* The offset of the call. */
     uint32_t offset;
 
     CallsiteCloneKey(JSFunction *f, JSScript *s, uint32_t o) : original(f), script(s), offset(o) {}
 
     typedef CallsiteCloneKey Lookup;
 
     static inline uint32_t hash(CallsiteCloneKey key) {
-        return uint32_t(size_t(key.script->code + key.offset) ^ size_t(key.original));
+        return uint32_t(size_t(key.script->getCode() + key.offset) ^ size_t(key.original));
     }
 
     static inline bool match(const CallsiteCloneKey &a, const CallsiteCloneKey &b) {
         return a.script == b.script && a.offset == b.offset && a.original == b.original;
     }
 };
 
 typedef HashMap<CallsiteCloneKey,
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -17,17 +17,17 @@ JSCompartment::initGlobal(js::GlobalObje
     JS_ASSERT(global.compartment() == this);
     JS_ASSERT(!global_);
     global_ = &global;
 }
 
 js::GlobalObject *
 JSCompartment::maybeGlobal() const
 {
-    JS_ASSERT_IF(global_, global_->compartment() == this);
+    JS_ASSERT_IF(global_ && !runtime_->heapProtected(), global_->compartment() == this);
     return global_;
 }
 
 js::AutoCompartment::AutoCompartment(ExclusiveContext *cx, JSObject *target)
   : cx_(cx),
     origin_(cx->compartment_)
 {
     cx_->enterCompartment(target->compartment());
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -60,17 +60,29 @@ class JSFunction : public JSObject
     static void staticAsserts() {
         JS_STATIC_ASSERT(INTERPRETED == JS_FUNCTION_INTERPRETED_BIT);
         static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
                       "shadow interface must match actual interface");
     }
 
     uint16_t        nargs;        /* number of formal arguments
                                      (including defaults and the rest parameter unlike f.length) */
+
+    uint16_t getNargs() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
+        return nargs;
+    }
+
     uint16_t        flags;        /* bitfield composed of the above Flags enum */
+
+    uint16_t getFlags() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
+        return flags;
+    }
+
     union U {
         class Native {
             friend class JSFunction;
             js::Native          native;       /* native method pointer or null */
             const JSJitInfo     *jitinfo;     /* Information about this function to be
                                                  used by the JIT;
                                                  use the accessor! */
         } n;
@@ -93,41 +105,41 @@ class JSFunction : public JSObject
     /* Call objects must be created for each invocation of a heavyweight function. */
     bool isHeavyweight() const {
         JS_ASSERT(!isInterpretedLazy());
 
         if (isNative())
             return false;
 
         // Note: this should be kept in sync with FunctionBox::isHeavyweight().
-        return nonLazyScript()->bindings.hasAnyAliasedBindings() ||
-               nonLazyScript()->funHasExtensibleScope ||
-               nonLazyScript()->funNeedsDeclEnvObject;
+        return nonLazyScript()->getHasAnyAliasedBindings() ||
+               nonLazyScript()->getFunHasExtensibleScope() ||
+               nonLazyScript()->getFunNeedsDeclEnvObject();
     }
 
     /* A function can be classified as either native (C++) or interpreted (JS): */
-    bool isInterpreted()            const { return flags & (INTERPRETED | INTERPRETED_LAZY); }
+    bool isInterpreted()            const { return getFlags() & (INTERPRETED | INTERPRETED_LAZY); }
     bool isNative()                 const { return !isInterpreted(); }
 
     /* Possible attributes of a native function: */
-    bool isNativeConstructor()      const { return flags & NATIVE_CTOR; }
+    bool isNativeConstructor()      const { return getFlags() & NATIVE_CTOR; }
 
     /* Possible attributes of an interpreted function: */
-    bool isFunctionPrototype()      const { return flags & IS_FUN_PROTO; }
-    bool isInterpretedLazy()        const { return flags & INTERPRETED_LAZY; }
-    bool hasScript()                const { return flags & INTERPRETED; }
-    bool isExprClosure()            const { return flags & EXPR_CLOSURE; }
-    bool hasGuessedAtom()           const { return flags & HAS_GUESSED_ATOM; }
-    bool isLambda()                 const { return flags & LAMBDA; }
-    bool isSelfHostedBuiltin()      const { return flags & SELF_HOSTED; }
-    bool isSelfHostedConstructor()  const { return flags & SELF_HOSTED_CTOR; }
-    bool hasRest()                  const { return flags & HAS_REST; }
+    bool isFunctionPrototype()      const { return getFlags() & IS_FUN_PROTO; }
+    bool isInterpretedLazy()        const { return getFlags() & INTERPRETED_LAZY; }
+    bool hasScript()                const { return getFlags() & INTERPRETED; }
+    bool isExprClosure()            const { return getFlags() & EXPR_CLOSURE; }
+    bool hasGuessedAtom()           const { return getFlags() & HAS_GUESSED_ATOM; }
+    bool isLambda()                 const { return getFlags() & LAMBDA; }
+    bool isSelfHostedBuiltin()      const { return getFlags() & SELF_HOSTED; }
+    bool isSelfHostedConstructor()  const { return getFlags() & SELF_HOSTED_CTOR; }
+    bool hasRest()                  const { return getFlags() & HAS_REST; }
     bool isWrappable()              const {
-        JS_ASSERT_IF(flags & SH_WRAPPABLE, isSelfHostedBuiltin());
-        return flags & SH_WRAPPABLE;
+        JS_ASSERT_IF(getFlags() & SH_WRAPPABLE, isSelfHostedBuiltin());
+        return getFlags() & SH_WRAPPABLE;
     }
 
     bool hasJITCode() const {
         if (!hasScript())
             return false;
 
         return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
     }
@@ -138,30 +150,30 @@ class JSFunction : public JSObject
     // creates a function object that gets stored with the enclosing script;
     // and (2) at run time the script's function object is cloned.
     //
     // But then, unlike other functions, (3) a bound function is created with
     // the clone as its target.
     //
     // isArrow() is true for all three of these Function objects.
     // isBoundFunction() is true only for the last one.
-    bool isArrow()                  const { return flags & ARROW; }
+    bool isArrow()                  const { return getFlags() & ARROW; }
 
     /* Compound attributes: */
     bool isBuiltin() const {
         return isNative() || isSelfHostedBuiltin();
     }
     bool isInterpretedConstructor() const {
         // Note: the JITs inline this check, so be careful when making changes
         // here. See IonMacroAssembler::branchIfNotInterpretedConstructor.
         return isInterpreted() && !isFunctionPrototype() &&
                (!isSelfHostedBuiltin() || isSelfHostedConstructor());
     }
     bool isNamedLambda() const {
-        return isLambda() && atom_ && !hasGuessedAtom();
+        return isLambda() && displayAtom() && !hasGuessedAtom();
     }
     bool hasParallelNative() const {
         return isNative() && jitInfo() && !!jitInfo()->parallelNative;
     }
 
     bool isBuiltinFunctionConstructor();
 
     /* Returns the strictness of this function, which must be interpreted. */
@@ -203,17 +215,21 @@ class JSFunction : public JSObject
     // Can be called multiple times by the parser.
     void setIsExprClosure() {
         flags |= EXPR_CLOSURE;
     }
 
     JSAtom *atom() const { return hasGuessedAtom() ? nullptr : atom_.get(); }
     js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName(); }
     void initAtom(JSAtom *atom) { atom_.init(atom); }
-    JSAtom *displayAtom() const { return atom_; }
+
+    JSAtom *displayAtom() const {
+        js::AutoUnprotectCell unprotect(this);
+        return atom_;
+    }
 
     void setGuessedAtom(JSAtom *atom) {
         JS_ASSERT(atom_ == nullptr);
         JS_ASSERT(atom != nullptr);
         JS_ASSERT(!hasGuessedAtom());
         atom_ = atom;
         flags |= HAS_GUESSED_ATOM;
     }
@@ -222,16 +238,17 @@ class JSFunction : public JSObject
     enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
 
     /*
      * For an interpreted function, accessors for the initial scope object of
      * activations (stack frames) of the function.
      */
     JSObject *environment() const {
         JS_ASSERT(isInterpreted());
+        js::AutoUnprotectCell unprotect(this);
         return u.i.env_;
     }
 
     void setEnvironment(JSObject *obj) {
         JS_ASSERT(isInterpreted());
         *(js::HeapPtrObject *)&u.i.env_ = obj;
     }
 
@@ -294,31 +311,34 @@ class JSFunction : public JSObject
             flags |= INTERPRETED;
             initScript(script);
         }
         JS_ASSERT(hasScript());
         return u.i.s.script_;
     }
 
     JSScript *nonLazyScript() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT(hasScript());
         return u.i.s.script_;
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
         return *(js::HeapPtrScript *)&u.i.s.script_;
     }
 
     js::LazyScript *lazyScript() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
         return u.i.s.lazy_;
     }
 
     js::LazyScript *lazyScriptOrNull() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT(isInterpretedLazy());
         return u.i.s.lazy_;
     }
 
     js::GeneratorKind generatorKind() const {
         if (!isInterpreted())
             return js::NotGenerator;
         if (hasScript())
@@ -349,16 +369,17 @@ class JSFunction : public JSObject
         JS_ASSERT(isInterpreted());
         flags &= ~INTERPRETED;
         flags |= INTERPRETED_LAZY;
         u.i.s.lazy_ = lazy;
     }
 
     JSNative native() const {
         JS_ASSERT(isNative());
+        js::AutoUnprotectCell unprotect(this);
         return u.n.native;
     }
 
     JSNative maybeNative() const {
         return isInterpreted() ? nullptr : native();
     }
 
     JSParallelNative parallelNative() const {
@@ -373,16 +394,17 @@ class JSFunction : public JSObject
     void initNative(js::Native native, const JSJitInfo *jitinfo) {
         JS_ASSERT(native);
         u.n.native = native;
         u.n.jitinfo = jitinfo;
     }
 
     const JSJitInfo *jitInfo() const {
         JS_ASSERT(isNative());
+        js::AutoUnprotectCell unprotect(this);
         return u.n.jitinfo;
     }
 
     void setJitInfo(const JSJitInfo *data) {
         JS_ASSERT(isNative());
         u.n.jitinfo = data;
     }
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -682,32 +682,34 @@ types::NewCompilerConstraintList(jit::Te
 }
 
 /* static */ bool
 TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
                            TemporaryTypeSet **pThisTypes,
                            TemporaryTypeSet **pArgTypes,
                            TemporaryTypeSet **pBytecodeTypes)
 {
+    AutoUnprotectCellUnderCompilationLock unprotect(script);
+
     LifoAlloc *alloc = IonAlloc();
     StackTypeSet *existing = script->types->typeArray();
 
     size_t count = NumTypeSets(script);
     TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
     if (!types)
         return false;
     PodZero(types, count);
 
     for (size_t i = 0; i < count; i++) {
         if (!existing[i].clone(alloc, &types[i]))
             return false;
     }
 
     *pThisTypes = types + (ThisTypes(script) - existing);
-    *pArgTypes = (script->function() && script->function()->nargs)
+    *pArgTypes = (script->function() && script->function()->getNargs())
                  ? (types + (ArgTypes(script, 0) - existing))
                  : nullptr;
     *pBytecodeTypes = types;
 
     constraints->freezeScript(script, *pThisTypes, *pArgTypes, *pBytecodeTypes);
     return true;
 }
 
@@ -779,39 +781,36 @@ CompilerConstraintInstance<T>::generateT
     return true;
 }
 
 } /* anonymous namespace */
 
 const Class *
 TypeObjectKey::clasp()
 {
-    return isTypeObject() ? asTypeObject()->clasp : asSingleObject()->getClass();
+    return isTypeObject() ? asTypeObject()->getClass() : asSingleObject()->getClass();
 }
 
 TaggedProto
 TypeObjectKey::proto()
 {
-    return isTypeObject() ? TaggedProto(asTypeObject()->proto) : asSingleObject()->getTaggedProto();
+    return isTypeObject() ? TaggedProto(asTypeObject()->getProto()) : asSingleObject()->getTaggedProto();
 }
 
 JSObject *
 TypeObjectKey::singleton()
 {
-    return isTypeObject() ? asTypeObject()->singleton : asSingleObject();
+    return isTypeObject() ? asTypeObject()->getSingleton() : asSingleObject();
 }
 
 TypeNewScript *
 TypeObjectKey::newScript()
 {
-    if (isTypeObject()) {
-        TypeObjectAddendum *addendum = asTypeObject()->addendum;
-        if (addendum && addendum->isNewScript())
-            return addendum->asNewScript();
-    }
+    if (isTypeObject() && asTypeObject()->hasNewScript())
+        return asTypeObject()->newScript();
     return nullptr;
 }
 
 TypeObject *
 TypeObjectKey::maybeType()
 {
     if (isTypeObject())
         return asTypeObject();
@@ -1625,24 +1624,21 @@ TemporaryTypeSet::getCommonPrototype()
 {
     if (unknownObject())
         return nullptr;
 
     JSObject *proto = nullptr;
     unsigned count = getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
-        TaggedProto nproto;
-        if (JSObject *object = getSingleObject(i))
-            nproto = object->getProto();
-        else if (TypeObject *object = getTypeObject(i))
-            nproto = object->proto.get();
-        else
+        TypeObjectKey *object = getObject(i);
+        if (!object)
             continue;
 
+        TaggedProto nproto = object->proto();
         if (proto) {
             if (nproto != proto)
                 return nullptr;
         } else {
             if (!nproto.isObject())
                 return nullptr;
             proto = nproto.toObject();
         }
@@ -3374,17 +3370,17 @@ types::TypeMonitorResult(JSContext *cx, 
 }
 
 bool
 types::UseNewTypeForClone(JSFunction *fun)
 {
     if (!fun->isInterpreted())
         return false;
 
-    if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite)
+    if (fun->hasScript() && fun->nonLazyScript()->getShouldCloneAtCallsite())
         return true;
 
     if (fun->isArrow())
         return false;
 
     if (fun->hasSingletonType())
         return false;
 
@@ -3409,20 +3405,20 @@ types::UseNewTypeForClone(JSFunction *fu
      * initialize method. We capture this, along with similar cases, by looking
      * for short scripts which use both .apply and arguments. For such scripts,
      * whenever creating a new instance of the function we both give that
      * instance a singleton type and clone the underlying script.
      */
 
     uint32_t begin, end;
     if (fun->hasScript()) {
-        if (!fun->nonLazyScript()->usesArgumentsAndApply)
+        if (!fun->nonLazyScript()->getUsesArgumentsAndApply())
             return false;
-        begin = fun->nonLazyScript()->sourceStart;
-        end = fun->nonLazyScript()->sourceEnd;
+        begin = fun->nonLazyScript()->getSourceStart();
+        end = fun->nonLazyScript()->getSourceEnd();
     } else {
         if (!fun->lazyScript()->usesArgumentsAndApply())
             return false;
         begin = fun->lazyScript()->begin();
         end = fun->lazyScript()->end();
     }
 
     return end - begin <= 100;
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -869,32 +869,50 @@ struct TypeTypedObject : public TypeObje
  */
 
 /* Type information about an object accessed by a script. */
 struct TypeObject : gc::BarrieredCell<TypeObject>
 {
     /* Class shared by objects using this type. */
     const Class *clasp;
 
+    const Class *getClass() {
+        AutoUnprotectCell unprotect(this);
+        return clasp;
+    }
+
     /* Prototype shared by objects using this type. */
     HeapPtrObject proto;
 
+    JSObject *getProto() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
+        return proto;
+    }
+
     /*
      * Whether there is a singleton JS object with this type. That JS object
      * must appear in type sets instead of this; we include the back reference
      * here to allow reverting the JS object to a lazy type.
      */
     HeapPtrObject singleton;
 
+    JSObject *getSingleton() {
+        AutoUnprotectCell unprotect(this);
+        return singleton;
+    }
+
     /*
      * Value held by singleton if this is a standin type for a singleton JS
      * object whose type has not been constructed yet.
      */
     static const size_t LAZY_SINGLETON = 1;
-    bool lazy() const { return singleton == (JSObject *) LAZY_SINGLETON; }
+    bool lazy() const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
+        return singleton == (JSObject *) LAZY_SINGLETON;
+    }
 
     /* Flags for this object. */
     TypeObjectFlags flags;
 
     /*
      * This field allows various special classes of objects to attach
      * additional information to a type object:
      *
@@ -902,28 +920,32 @@ struct TypeObject : gc::BarrieredCell<Ty
      *   indicates that objects of this type have always been
      *   constructed using 'new' on the specified script, which adds
      *   some number of properties to the object in a definite order
      *   before the object escapes.
      */
     HeapPtr<TypeObjectAddendum> addendum;
 
     bool hasNewScript() const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return addendum && addendum->isNewScript();
     }
 
     TypeNewScript *newScript() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return addendum->asNewScript();
     }
 
     bool hasTypedObject() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return addendum && addendum->isTypedObject();
     }
 
     TypeTypedObject *typedObject() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return addendum->asTypedObject();
     }
 
     /*
      * Tag the type object for a binary data type descriptor, instance,
      * or handle with the type representation of the data it points at.
      * If this type object is already tagged with a binary data addendum,
      * this addendum must already be associated with the same TypeRepresentation,
@@ -961,32 +983,40 @@ struct TypeObject : gc::BarrieredCell<Ty
      * defineProperty which are on native properties, and on any jitcode which
      * might update the property with a new type.
      */
     Property **propertySet;
 
     /* If this is an interpreted function, the function object. */
     HeapPtrFunction interpretedFunction;
 
+    JSFunction *getInterpretedFunction() {
+        AutoUnprotectCell unprotect(this);
+        return interpretedFunction;
+    }
+
 #if JS_BITS_PER_WORD == 32
     uint32_t padding;
 #endif
 
     inline TypeObject(const Class *clasp, TaggedProto proto, bool unknown);
 
     bool hasAnyFlags(TypeObjectFlags flags) {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return !!(this->flags & flags);
     }
     bool hasAllFlags(TypeObjectFlags flags) {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return (this->flags & flags) == flags;
     }
 
     bool unknownProperties() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT_IF(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES,
                      hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
         return !!(flags & OBJECT_FLAG_UNKNOWN_PROPERTIES);
     }
 
     /*
      * Get or create a property of this object. Only call this for properties which
      * a script accesses explicitly.
@@ -1011,17 +1041,20 @@ struct TypeObject : gc::BarrieredCell<Ty
     /*
      * NewObjectCache is used when we take a stub for allocation. It is used
      * more rarely, but still in hot paths, so pre-tenure with fewer uses.
      */
     const static uint32_t MaxCachedAllocTenures = 64;
 
     /* Returns true if the allocating script should be recompiled. */
     bool incrementTenureCount();
+
     uint32_t tenureCount() const {
+        // Note: We ignore races when reading the tenure count of a type off thread.
+        AutoUnprotectCell unprotect(this);
         return (flags & OBJECT_FLAG_TENURE_COUNT_MASK) >> OBJECT_FLAG_TENURE_COUNT_SHIFT;
     }
 
     bool isLongLivedForCachedAlloc() const {
         return tenureCount() >= MaxCachedAllocTenures;
     }
 
     bool isLongLivedForJITAlloc() const {
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -127,18 +127,18 @@ Type::ObjectType(JSObject *obj)
     if (obj->hasSingletonType())
         return Type(uintptr_t(obj) | 1);
     return Type(uintptr_t(obj->type()));
 }
 
 /* static */ inline Type
 Type::ObjectType(TypeObject *obj)
 {
-    if (obj->singleton)
-        return Type(uintptr_t(obj->singleton.get()) | 1);
+    if (obj->getSingleton())
+        return Type(uintptr_t(obj->getSingleton()) | 1);
     return Type(uintptr_t(obj));
 }
 
 /* static */ inline Type
 Type::ObjectType(TypeObjectKey *obj)
 {
     return Type(uintptr_t(obj));
 }
@@ -216,17 +216,18 @@ IdToTypeId(jsid id)
     if (JSID_IS_INT(id))
         return JSID_VOID;
 
     /*
      * Check for numeric strings, as in js_StringIsIndex, but allow negative
      * and overflowing integers.
      */
     if (JSID_IS_STRING(id)) {
-        JSFlatString *str = JSID_TO_FLAT_STRING(id);
+        JSAtom *str = JSID_TO_ATOM(id);
+        AutoUnprotectCell unprotect(str);
         JS::TwoByteChars cp = str->range();
         if (JS7_ISDEC(cp[0]) || cp[0] == '-') {
             for (size_t i = 1; i < cp.length(); ++i) {
                 if (!JS7_ISDEC(cp[i]))
                     return id;
             }
             return JSID_VOID;
         }
@@ -604,47 +605,47 @@ TypeScript::ThisTypes(JSScript *script)
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
 /* static */ inline StackTypeSet *
 TypeScript::ArgTypes(JSScript *script, unsigned i)
 {
-    JS_ASSERT(i < script->function()->nargs);
+    JS_ASSERT(i < script->function()->getNargs());
     return script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
 }
 
 template <typename TYPESET>
 /* static */ inline TYPESET *
 TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
 {
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
 #ifdef JS_ION
     uint32_t *bytecodeMap = script->baselineScript()->bytecodeTypeMap();
 #else
     uint32_t *bytecodeMap = nullptr;
     MOZ_CRASH();
 #endif
-    uint32_t offset = pc - script->code;
-    JS_ASSERT(offset < script->length);
+    uint32_t offset = pc - script->getCode();
+    JS_ASSERT(offset < script->getLength());
 
     // See if this pc is the next typeset opcode after the last one looked up.
-    if (bytecodeMap[*hint + 1] == offset && (*hint + 1) < script->nTypeSets) {
+    if (bytecodeMap[*hint + 1] == offset && (*hint + 1) < script->getNumTypeSets()) {
         (*hint)++;
         return typeArray + *hint;
     }
 
     // See if this pc is the same as the last one looked up.
     if (bytecodeMap[*hint] == offset)
         return typeArray + *hint;
 
     // Fall back to a binary search.
     size_t bottom = 0;
-    size_t top = script->nTypeSets - 1;
+    size_t top = script->getNumTypeSets() - 1;
     size_t mid = bottom + (top - bottom) / 2;
     while (mid < top) {
         if (bytecodeMap[mid] < offset)
             bottom = mid + 1;
         else if (bytecodeMap[mid] > offset)
             top = mid;
         else
             break;
@@ -1176,17 +1177,17 @@ TypeSet::addType(Type type, LifoAlloc *a
         setBaseObjectCount(objectCount);
 
         if (objectCount == TYPE_FLAG_OBJECT_COUNT_LIMIT)
             goto unknownObject;
     }
 
     if (type.isTypeObject()) {
         TypeObject *nobject = type.typeObject();
-        JS_ASSERT(!nobject->singleton);
+        JS_ASSERT(!nobject->getSingleton());
         if (nobject->unknownProperties())
             goto unknownObject;
     }
 
     if (false) {
     unknownObject:
         type = Type::AnyObjectType();
         flags |= TYPE_FLAG_ANYOBJECT;
@@ -1308,17 +1309,17 @@ TypeSet::getTypeOrSingleObject(JSContext
 }
 
 inline const Class *
 TypeSet::getObjectClass(unsigned i) const
 {
     if (JSObject *object = getSingleObject(i))
         return object->getClass();
     if (TypeObject *object = getTypeObject(i))
-        return object->clasp;
+        return object->getClass();
     return nullptr;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 inline TypeObject::TypeObject(const Class *clasp, TaggedProto proto, bool unknown)
@@ -1397,16 +1398,18 @@ TypeObject::getProperty(ExclusiveContext
 
 inline HeapTypeSet *
 TypeObject::maybeGetProperty(jsid id)
 {
     JS_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id));
     JS_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id));
     JS_ASSERT(!unknownProperties());
 
+    AutoUnprotectCellUnderCompilationLock unprotect(this);
+
     Property *prop = HashSetLookup<jsid,Property,Property>
         (propertySet, basePropertyCount(), id);
 
     return prop ? &prop->types : nullptr;
 }
 
 inline unsigned
 TypeObject::getPropertyCount()
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5549,16 +5549,18 @@ DumpProperty(JSObject *obj, Shape &shape
         fprintf(stderr, " (INVALID!)");
     }
     fprintf(stderr, "\n");
 }
 
 bool
 JSObject::uninlinedIsProxy() const
 {
+    AutoUnprotectCellUnderCompilationLock unprotect0(this);
+    AutoUnprotectCellUnderCompilationLock unprotect1(type_);
     return is<ProxyObject>();
 }
 
 void
 JSObject::dump()
 {
     JSObject *obj = this;
     fprintf(stderr, "object %p\n", (void *) obj);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -283,16 +283,20 @@ class JSObject : public js::ObjectImpl
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
   public:
     bool setDelegate(js::ExclusiveContext *cx) {
         return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
     }
 
     bool isBoundFunction() const {
+        // Note: This function can race when it is called during off thread compilation.
+        js::AutoUnprotectCell unprotect0(this);
+        js::AutoUnprotectCell unprotect1(lastProperty());
+        js::AutoUnprotectCell unprotect2(lastProperty()->base());
         return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
     }
 
     inline bool hasSpecialEquality() const;
 
     bool watched() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
     }
@@ -344,39 +348,43 @@ class JSObject : public js::ObjectImpl
     bool isIndexed() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED);
     }
 
     uint32_t propertyCount() const {
         return lastProperty()->entryCount();
     }
 
+    uint32_t propertyCountForCompilation() const {
+        return lastProperty()->entryCountForCompilation();
+    }
+
     bool hasShapeTable() const {
         return lastProperty()->hasTable();
     }
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes);
 
     bool hasIdempotentProtoChain() const;
 
     static const uint32_t MAX_FIXED_SLOTS = 16;
 
   public:
 
     /* Accessors for properties. */
 
     /* Whether a slot is at a fixed offset from this object. */
     bool isFixedSlot(size_t slot) {
-        return slot < numFixedSlots();
+        return slot < numFixedSlotsForCompilation();
     }
 
     /* Index into the dynamic slots array to use for a dynamic slot. */
     size_t dynamicSlotIndex(size_t slot) {
-        JS_ASSERT(slot >= numFixedSlots());
-        return slot - numFixedSlots();
+        JS_ASSERT(slot >= numFixedSlotsForCompilation());
+        return slot - numFixedSlotsForCompilation();
     }
 
     /*
      * 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(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount,
@@ -735,16 +743,21 @@ class JSObject : public js::ObjectImpl
         memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value));
     }
 
     bool shouldConvertDoubleElements() {
         JS_ASSERT(isNative());
         return getElementsHeader()->shouldConvertDoubleElements();
     }
 
+    bool shouldConvertDoubleElementsForCompilation() {
+        // Note: isNative() generally can't be safely called off thread.
+        return getElementsHeader()->shouldConvertDoubleElements();
+    }
+
     inline void setShouldConvertDoubleElements();
 
     /* Packed information for this object's elements. */
     inline bool writeToIndexWouldMarkNotPacked(uint32_t index);
     inline void markDenseElementsNotPacked(js::ExclusiveContext *cx);
 
     /*
      * ensureDenseElements ensures that the object can hold at least
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -121,17 +121,20 @@ static uint32_t
 NumBlockSlots(JSScript *script, jsbytecode *pc)
 {
     JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
               *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
 
-    return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().slotCount();
+    StaticBlockObject *block = &script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>();
+    AutoUnprotectCell unprotect(block);
+
+    return block->propertyCountForCompilation();
 }
 
 unsigned
 js::StackUses(JSScript *script, jsbytecode *pc)
 {
     JSOp op = (JSOp) *pc;
     const JSCodeSpec &cs = js_CodeSpec[op];
     if (cs.nuses >= 0)
--- a/js/src/jsopcodeinlines.h
+++ b/js/src/jsopcodeinlines.h
@@ -11,18 +11,18 @@
 
 #include "jsscript.h"
 
 namespace js {
 
 static inline unsigned
 GetDefCount(JSScript *script, unsigned offset)
 {
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
+    JS_ASSERT(offset < script->getLength());
+    jsbytecode *pc = script->getCode() + offset;
 
     /*
      * Add an extra pushed value for OR/AND opcodes, so that they are included
      * in the pushed array of stack values for type inference.
      */
     switch (JSOp(*pc)) {
       case JSOP_OR:
       case JSOP_AND:
@@ -38,18 +38,18 @@ GetDefCount(JSScript *script, unsigned o
       default:
         return StackDefs(script, pc);
     }
 }
 
 static inline unsigned
 GetUseCount(JSScript *script, unsigned offset)
 {
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
+    JS_ASSERT(offset < script->getLength());
+    jsbytecode *pc = script->getCode() + offset;
 
     if (JSOp(*pc) == JSOP_PICK)
         return (pc[1] + 1);
     if (js_CodeSpec[*pc].nuses == -1)
         return StackUses(script, pc);
     return js_CodeSpec[*pc].nuses;
 }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2087,21 +2087,21 @@ GSNCache::purge()
     code = nullptr;
     if (map.initialized())
         map.finish();
 }
 
 jssrcnote *
 js::GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc)
 {
-    size_t target = pc - script->code;
-    if (target >= size_t(script->length))
+    size_t target = pc - script->getCode();
+    if (target >= size_t(script->getLength()))
         return nullptr;
 
-    if (cache.code == script->code) {
+    if (cache.code == script->getCode()) {
         JS_ASSERT(cache.map.initialized());
         GSNCache::Map::Ptr p = cache.map.lookup(pc);
         return p ? p->value : nullptr;
     }
 
     size_t offset = 0;
     jssrcnote *result;
     for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) {
@@ -2111,37 +2111,37 @@ js::GetSrcNote(GSNCache &cache, JSScript
         }
         offset += SN_DELTA(sn);
         if (offset == target && SN_IS_GETTABLE(sn)) {
             result = sn;
             break;
         }
     }
 
-    if (cache.code != script->code && script->length >= GSN_CACHE_THRESHOLD) {
+    if (cache.code != script->getCode() && script->getLength() >= GSN_CACHE_THRESHOLD) {
         unsigned nsrcnotes = 0;
         for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
              sn = SN_NEXT(sn)) {
             if (SN_IS_GETTABLE(sn))
                 ++nsrcnotes;
         }
         if (cache.code) {
             JS_ASSERT(cache.map.initialized());
             cache.map.finish();
             cache.code = nullptr;
         }
         if (cache.map.init(nsrcnotes)) {
-            pc = script->code;
+            pc = script->getCode();
             for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
                  sn = SN_NEXT(sn)) {
                 pc += SN_DELTA(sn);
                 if (SN_IS_GETTABLE(sn))
                     JS_ALWAYS_TRUE(cache.map.put(pc, sn));
             }
-            cache.code = script->code;
+            cache.code = script->getCode();
         }
     }
 
     return result;
 }
 
 jssrcnote *
 js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc)
@@ -2197,17 +2197,17 @@ js::PCToLineNumber(unsigned startLine, j
 
 unsigned
 js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp)
 {
     /* Cope with StackFrame.pc value prior to entering js_Interpret. */
     if (!pc)
         return 0;
 
-    return PCToLineNumber(script->lineno, script->notes(), script->code, pc, columnp);
+    return PCToLineNumber(script->getLineno(), script->notes(), script->getCode(), pc, columnp);
 }
 
 /* The line number limit is the same as the jssrcnote offset limit. */
 #define SN_LINE_LIMIT   (SN_3BYTE_OFFSET_FLAG << 16)
 
 jsbytecode *
 js_LineNumberToPC(JSScript *script, unsigned target)
 {
@@ -2951,22 +2951,24 @@ JSScript::argumentsOptimizationFailed(JS
     }
 
     return true;
 }
 
 bool
 JSScript::varIsAliased(unsigned varSlot)
 {
+    AutoUnprotectCell unprotect(this);
     return bindings.bindingIsAliased(bindings.numArgs() + varSlot);
 }
 
 bool
 JSScript::formalIsAliased(unsigned argSlot)
 {
+    AutoUnprotectCell unprotect(this);
     return bindings.bindingIsAliased(argSlot);
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -179,21 +179,23 @@ class Bindings
      * memory. To avoid tracing these bindings during GC, we keep track of
      * whether the bindings are temporary or permanent in the low bit of
      * bindingArrayAndFlag_.
      */
     static const uintptr_t TEMPORARY_STORAGE_BIT = 0x1;
     bool bindingArrayUsingTemporaryStorage() const {
         return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT;
     }
+
+  public:
+
     Binding *bindingArray() const {
         return reinterpret_cast<Binding *>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
     }
 
-  public:
     inline Bindings();
 
     /*
      * Initialize a Bindings with a pointer into temporary storage.
      * bindingArray must have length numArgs+numVars. Before the temporary
      * storage is release, switchToScriptStorage must be called, providing a
      * pointer into the Binding array stored in script->data.
      */
@@ -219,17 +221,24 @@ class Bindings
 
     /* Convenience method to get the var index of 'arguments'. */
     static unsigned argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle);
 
     /* Return whether the binding at bindingIndex is aliased. */
     bool bindingIsAliased(unsigned bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
-    bool hasAnyAliasedBindings() const { return callObjShape_ && !callObjShape_->isEmptyShape(); }
+    bool hasAnyAliasedBindings() const {
+        if (!callObjShape_)
+            return false;
+
+        // Binding shapes are immutable once constructed.
+        AutoUnprotectCell unprotect(callObjShape_);
+        return !callObjShape_->isEmptyShape();
+    }
 
     void trace(JSTracer *trc);
 };
 
 template <>
 struct GCMethods<Bindings> {
     static Bindings initial();
     static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
@@ -423,16 +432,20 @@ class ScriptSourceObject : public JSObje
 {
   public:
     static const Class class_;
 
     static void finalize(FreeOp *fop, JSObject *obj);
     static ScriptSourceObject *create(ExclusiveContext *cx, ScriptSource *source);
 
     ScriptSource *source() {
+        // Script source objects are immutable.
+        AutoUnprotectCell unprotect0(this);
+        AutoUnprotectCell unprotect1(lastProperty());
+        AutoUnprotectCell unprotect2(lastProperty()->base());
         return static_cast<ScriptSource *>(getReservedSlot(SOURCE_SLOT).toPrivate());
     }
 
     void setSource(ScriptSource *source);
 
   private:
     static const uint32_t SOURCE_SLOT = 0;
 };
@@ -464,20 +477,41 @@ class JSScript : public js::gc::Barriere
     //
 
     // Larger-than-word-sized fields.
 
   public:
     js::Bindings    bindings;   /* names of top-level variables in this script
                                    (and arguments if this is a function script) */
 
+    bool getHasAnyAliasedBindings() const {
+        js::AutoUnprotectCell unprotect(this);
+        return bindings.hasAnyAliasedBindings();
+    }
+
+    js::Binding *bindingArray() const {
+        js::AutoUnprotectCell unprotect(this);
+        return bindings.bindingArray();
+    }
+
+    unsigned numArgs() const {
+        js::AutoUnprotectCell unprotect(this);
+        return bindings.numArgs();
+    }
+
     // Word-sized fields.
 
   public:
     jsbytecode      *code;      /* bytecodes and their immediate operands */
+
+    jsbytecode *getCode() {
+        js::AutoUnprotectCell unprotect(this);
+        return code;
+    }
+
     uint8_t         *data;      /* pointer to variable-length data array (see
                                    comment above Create() for details) */
 
     js::HeapPtrAtom *atoms;     /* maps immediate index to literal struct */
 
     JSCompartment   *compartment_;
 
     /* Persistent type information retained across GCs. */
@@ -505,30 +539,56 @@ class JSScript : public js::gc::Barriere
     uint8_t *baselineOrIonRaw;
     uint8_t *baselineOrIonSkipArgCheck;
 
     // 32-bit fields.
 
   public:
     uint32_t        length;     /* length of code vector */
 
+    uint32_t getLength() {
+        js::AutoUnprotectCell unprotect(this);
+        return length;
+    }
+
     uint32_t        dataSize;   /* size of the used part of the data array */
 
     uint32_t        lineno;     /* base line number of script */
+
+    uint32_t getLineno() {
+        js::AutoUnprotectCell unprotect(this);
+        return lineno;
+    }
+
     uint32_t        column;     /* base column of script, optionally set */
 
     uint32_t        mainOffset; /* offset of main entry point from code, after
                                    predef'ing prolog */
 
+    uint32_t getMainOffset() {
+        js::AutoUnprotectCell unprotect(this);
+        return mainOffset;
+    }
+
     uint32_t        natoms;     /* length of atoms array */
 
     /* Range of characters in scriptSource which contains this script's source. */
     uint32_t        sourceStart;
     uint32_t        sourceEnd;
 
+    uint32_t getSourceStart() {
+        js::AutoUnprotectCell unprotect(this);
+        return sourceStart;
+    }
+
+    uint32_t getSourceEnd() {
+        js::AutoUnprotectCell unprotect(this);
+        return sourceEnd;
+    }
+
   private:
     uint32_t        useCount;   /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
 
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
     // printing analysis information.
@@ -544,20 +604,36 @@ class JSScript : public js::gc::Barriere
     uint16_t        version;    /* JS version under which script was compiled */
 
   public:
     uint16_t        funLength;  /* ES6 function length */
 
     uint16_t        nfixed;     /* number of slots besides stack operands in
                                    slot array */
 
+    uint16_t getNfixed() {
+        js::AutoUnprotectCell unprotect(this);
+        return nfixed;
+    }
+
     uint16_t        nTypeSets;  /* number of type sets used in this script for
                                    dynamic type monitoring */
 
+    uint16_t getNumTypeSets() {
+        js::AutoUnprotectCell unprotect(this);
+        return nTypeSets;
+    }
+
     uint16_t        nslots;     /* vars plus maximum stack depth */
+
+    uint16_t getNslots() {
+        js::AutoUnprotectCell unprotect(this);
+        return nslots;
+    }
+
     uint16_t        staticLevel;/* static level for display maintenance */
 
     // Bit fields.
 
   public:
     // The kinds of the optional arrays.
     enum ArrayKind {
         CONSTS,
@@ -579,60 +655,142 @@ class JSScript : public js::gc::Barriere
     // Unused padding; feel free to steal these if you need them.
     uint8_t         padToByte_:1;
 
     // 1-bit fields.
 
   public:
     bool            noScriptRval:1; /* no need for result value of last
                                        expression statement */
+
+    bool getNoScriptRval() const {
+        js::AutoUnprotectCell unprotect(this);
+        return noScriptRval;
+    }
+
     bool            savedCallerFun:1; /* can call getCallerFunction() */
     bool            strict:1; /* code is in strict mode */
+
+    bool getStrict() const {
+        js::AutoUnprotectCell unprotect(this);
+        return strict;
+    }
+
     bool            explicitUseStrict:1; /* code has "use strict"; explicitly */
     bool            compileAndGo:1;   /* see Parser::compileAndGo */
+
+    bool getCompileAndGo() const {
+        js::AutoUnprotectCell unprotect(this);
+        return compileAndGo;
+    }
+
     bool            selfHosted:1;     /* see Parser::selfHostingMode */
     bool            bindingsAccessedDynamically:1; /* see FunctionContextFlags */
     bool            funHasExtensibleScope:1;       /* see FunctionContextFlags */
+
+    bool getFunHasExtensibleScope() const {
+        js::AutoUnprotectCell unprotect(this);
+        return funHasExtensibleScope;
+    }
+
     bool            funNeedsDeclEnvObject:1;       /* see FunctionContextFlags */
+
+    bool getFunNeedsDeclEnvObject() const {
+        js::AutoUnprotectCell unprotect(this);
+        return funNeedsDeclEnvObject;
+    }
+
     bool            funHasAnyAliasedFormal:1;      /* true if any formalIsAliased(i) */
+
+    bool getFunHasAnyAliasedFormal() const {
+        js::AutoUnprotectCell unprotect(this);
+        return funHasAnyAliasedFormal;
+    }
+
     bool            warnedAboutUndefinedProp:1; /* have warned about uses of
                                                    undefined properties in this
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
     bool            treatAsRunOnce:1; /* script is a lambda to treat as running once. */
+
+    bool getTreatAsRunOnce() const {
+        js::AutoUnprotectCell unprotect(this);
+        return treatAsRunOnce;
+    }
+
     bool            hasRunOnce:1;     /* if treatAsRunOnce, whether script has executed. */
     bool            hasBeenCloned:1;  /* script has been reused for a clone. */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
 
     // Set for functions defined at the top level within an 'eval' script.
     bool directlyInsideEval:1;
 
     // Both 'arguments' and f.apply() are used. This is likely to be a wrapper.
     bool usesArgumentsAndApply:1;
 
+    bool getUsesArgumentsAndApply() const {
+        js::AutoUnprotectCell unprotect(this);
+        return usesArgumentsAndApply;
+    }
+
     /* script is attempted to be cloned anew at each callsite. This is
        temporarily needed for ParallelArray selfhosted code until type
        information can be made context sensitive. See discussion in
        bug 826148. */
     bool            shouldCloneAtCallsite:1;
+
+    bool getShouldCloneAtCallsite() const {
+        js::AutoUnprotectCell unprotect(this);
+        return shouldCloneAtCallsite;
+    }
+
     bool            isCallsiteClone:1; /* is a callsite clone; has a link to the original function */
     bool            shouldInline:1;    /* hint to inline when possible */
+
+    bool getShouldInline() const {
+        js::AutoUnprotectCell unprotect(this);
+        return shouldInline;
+    }
+
     bool            uninlineable:1;    /* explicitly marked as uninlineable */
-#ifdef JS_ION
+
+    bool getUninlineable() const {
+        js::AutoUnprotectCell unprotect(this);
+        return uninlineable;
+    }
+
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
+
+    bool getFailedBoundsCheck() const {
+        js::AutoUnprotectCell unprotect(this);
+        return failedBoundsCheck;
+    }
+
     bool            failedShapeGuard:1; /* script has had hoisted shape guard fail */
+
+    bool getFailedShapeGuard() const {
+        js::AutoUnprotectCell unprotect(this);
+        return failedShapeGuard;
+    }
+
     bool            hadFrequentBailouts:1;
-#else
-    bool            failedBoundsCheckPad:1;
-    bool            failedShapeGuardPad:1;
-    bool            hadFrequentBailoutsPad:1;
-#endif
+
+    bool getHadFrequentBailouts() const {
+        js::AutoUnprotectCell unprotect(this);
+        return hadFrequentBailouts;
+    }
+
     bool            invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */
 
+    bool getInvalidatedIdempotentCache() const {
+        js::AutoUnprotectCell unprotect(this);
+        return invalidatedIdempotentCache;
+    }
+
     // If the generator was created implicitly via a generator expression,
     // isGeneratorExp will be true.
     bool            isGeneratorExp:1;
 
     bool            hasScriptCounts:1;/* script has an entry in
                                          JSCompartment::scriptCountsMap */
     bool            hasDebugScript:1; /* script has an entry in
                                          JSCompartment::debugScriptMap */
@@ -673,21 +831,24 @@ class JSScript : public js::gc::Barriere
 
     inline JSPrincipals *principals();
 
     JSCompartment *compartment() const { return compartment_; }
 
     void setVersion(JSVersion v) { version = v; }
 
     /* See ContextFlags::funArgumentsHasLocalBinding comment. */
-    bool argumentsHasVarBinding() const { return argsHasVarBinding_; }
+    bool argumentsHasVarBinding() const {
+        js::AutoUnprotectCell unprotect(this);
+        return argsHasVarBinding_;
+    }
     jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
     void setArgumentsHasVarBinding();
     bool argumentsAliasesFormals() const {
-        return argumentsHasVarBinding() && !strict;
+        return argumentsHasVarBinding() && !getStrict();
     }
 
     js::GeneratorKind generatorKind() const {
         return js::GeneratorKindFromBits(generatorKindBits_);
     }
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
@@ -704,41 +865,45 @@ class JSScript : public js::gc::Barriere
      * needsArgsObj which is set by ScriptAnalysis::analyzeSSA before running
      * the script the first time. When !needsArgsObj, the prologue may simply
      * write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any
      * uses of 'arguments' will be guaranteed to handle this magic value.
      * So avoid spurious arguments object creation, we maintain the invariant
      * that needsArgsObj is only called after the script has been analyzed.
      */
     bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
-    bool needsArgsObj() const { JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_; }
+    bool needsArgsObj() const {
+        js::AutoUnprotectCell unprotect(this);
+        JS_ASSERT(analyzedArgsUsage()); return needsArgsObj_;
+    }
     void setNeedsArgsObj(bool needsArgsObj);
     static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script);
 
     /*
      * Arguments access (via JSOP_*ARG* opcodes) must access the canonical
      * location for the argument. If an arguments object exists AND this is a
      * non-strict function (where 'arguments' aliases formals), then all access
      * must go through the arguments object. Otherwise, the local slot is the
      * canonical location for the arguments. Note: if a formal is aliased
      * through the scope chain, then script->formalIsAliased and JSOP_*ARG*
      * opcodes won't be emitted at all.
      */
     bool argsObjAliasesFormals() const {
-        return needsArgsObj() && !strict;
+        return needsArgsObj() && !getStrict();
     }
 
     bool hasAnyIonScript() const {
         return hasIonScript() || hasParallelIonScript();
     }
 
     bool hasIonScript() const {
         return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
     }
     bool canIonCompile() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         return ion != ION_DISABLED_SCRIPT;
     }
 
     bool isIonCompilingOffThread() const {
         return ion == ION_COMPILING_SCRIPT;
     }
 
     js::jit::IonScript *ionScript() const {
@@ -754,34 +919,37 @@ class JSScript : public js::gc::Barriere
     void setIonScript(js::jit::IonScript *ionScript) {
         if (hasIonScript())
             js::jit::IonScript::writeBarrierPre(tenuredZone(), ion);
         ion = ionScript;
         updateBaselineOrIonRaw();
     }
 
     bool hasBaselineScript() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         return baseline && baseline != BASELINE_DISABLED_SCRIPT;
     }
     bool canBaselineCompile() const {
         return baseline != BASELINE_DISABLED_SCRIPT;
     }
     js::jit::BaselineScript *baselineScript() const {
         JS_ASSERT(hasBaselineScript());
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         return baseline;
     }
     inline void setBaselineScript(js::jit::BaselineScript *baselineScript);
 
     void updateBaselineOrIonRaw();
 
     bool hasParallelIonScript() const {
         return parallelIon && parallelIon != ION_DISABLED_SCRIPT && parallelIon != ION_COMPILING_SCRIPT;
     }
 
     bool canParallelIonCompile() const {
+        js::AutoUnprotectCellUnderCompilationLock unprotect(this);
         return parallelIon != ION_DISABLED_SCRIPT;
     }
 
     bool isParallelIonCompilingOffThread() const {
         return parallelIon == ION_COMPILING_SCRIPT;
     }
 
     js::jit::IonScript *parallelIonScript() const {
@@ -812,31 +980,37 @@ class JSScript : public js::gc::Barriere
     static size_t offsetOfBaselineOrIonSkipArgCheck() {
         return offsetof(JSScript, baselineOrIonSkipArgCheck);
     }
 
     /*
      * Original compiled function for the script, if it has a function.
      * nullptr for global and eval scripts.
      */
-    JSFunction *function() const { return function_; }
+    JSFunction *function() const {
+        js::AutoUnprotectCell unprotect(this);
+        return function_;
+    }
     inline void setFunction(JSFunction *fun);
 
     JSFunction *originalFunction() const;
     void setOriginalFunctionObject(JSObject *fun);
 
     JSFlatString *sourceData(JSContext *cx);
 
     static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked);
 
     void setSourceObject(js::ScriptSourceObject *object);
     js::ScriptSourceObject *sourceObject() const;
     js::ScriptSource *scriptSource() const { return sourceObject()->source(); }
     JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); }
-    const char *filename() const { return scriptSource()->filename(); }
+    const char *filename() const {
+        js::AutoUnprotectCell unprotect(this);
+        return scriptSource()->filename();
+    }
 
   public:
 
     /* Return whether this script was compiled for 'eval' */
     bool isForEval() { return isCachedEval || isActiveEval; }
 
 #ifdef DEBUG
     unsigned id();
@@ -860,16 +1034,17 @@ class JSScript : public js::gc::Barriere
     inline void clearAnalysis();
     inline js::analyze::ScriptAnalysis *analysis();
 
     inline js::GlobalObject &global() const;
     js::GlobalObject &uninlinedGlobal() const;
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
+        js::AutoUnprotectCell unprotect(this);
         if (isCallsiteClone)
             return nullptr;
         return enclosingScopeOrOriginalFunction_;
     }
 
     /*
      * If a compile error occurs in an enclosing function after parsing a
      * nested function, the enclosing function's JSFunction, which appears on
@@ -882,17 +1057,21 @@ class JSScript : public js::gc::Barriere
      */
     bool enclosingScriptsCompiledSuccessfully() const;
 
   private:
     bool makeTypes(JSContext *cx);
     bool makeAnalysis(JSContext *cx);
 
   public:
-    uint32_t getUseCount() const  { return useCount; }
+    uint32_t getUseCount() const {
+        // Note: We ignore races when reading the use count of a script off thread.
+        js::AutoUnprotectCell unprotect(this);
+        return useCount;
+    }
     uint32_t incUseCount(uint32_t amount = 1) { return useCount += amount; }
     uint32_t *addressOfUseCount() { return &useCount; }
     static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); }
     void resetUseCount() { useCount = 0; }
 
   public:
     bool initScriptCounts(JSContext *cx);
     js::PCCounts getPCCounts(jsbytecode *pc);
@@ -912,20 +1091,24 @@ class JSScript : public js::gc::Barriere
      */
     size_t computedSizeOfData() const;
     size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const;
     size_t sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const;
 
     uint32_t numNotes();  /* Number of srcnote slots in the srcnotes section */
 
     /* Script notes are allocated right after the code. */
-    jssrcnote *notes() { return (jssrcnote *)(code + length); }
+    jssrcnote *notes() { return (jssrcnote *)(getCode() + getLength()); }
 
-    bool hasArray(ArrayKind kind)           { return (hasArrayBits & (1 << kind)); }
-    void setHasArray(ArrayKind kind)        { hasArrayBits |= (1 << kind); }
+    bool hasArray(ArrayKind kind) {
+        js::AutoUnprotectCell unprotect(this);
+        return (hasArrayBits & (1 << kind));
+    }
+
+    void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); }
     void cloneHasArray(JSScript *script) { hasArrayBits = script->hasArrayBits; }
 
     bool hasConsts()        { return hasArray(CONSTS);      }
     bool hasObjects()       { return hasArray(OBJECTS);     }
     bool hasRegexps()       { return hasArray(REGEXPS);     }
     bool hasTrynotes()      { return hasArray(TRYNOTES);    }
     bool hasBlockScopes()   { return hasArray(BLOCK_SCOPES); }
 
@@ -934,42 +1117,47 @@ class JSScript : public js::gc::Barriere
     size_t constsOffset()     { return 0; }
     size_t objectsOffset()    { return OFF(constsOffset,     hasConsts,     js::ConstArray);      }
     size_t regexpsOffset()    { return OFF(objectsOffset,    hasObjects,    js::ObjectArray);     }
     size_t trynotesOffset()   { return OFF(regexpsOffset,    hasRegexps,    js::ObjectArray);     }
     size_t blockScopesOffset(){ return OFF(trynotesOffset,   hasTrynotes,   js::TryNoteArray);    }
 
     js::ConstArray *consts() {
         JS_ASSERT(hasConsts());
+        js::AutoUnprotectCell unprotect(this);
         return reinterpret_cast<js::ConstArray *>(data + constsOffset());
     }
 
     js::ObjectArray *objects() {
         JS_ASSERT(hasObjects());
+        js::AutoUnprotectCell unprotect(this);
         return reinterpret_cast<js::ObjectArray *>(data + objectsOffset());
     }
 
     js::ObjectArray *regexps() {
         JS_ASSERT(hasRegexps());
+        js::AutoUnprotectCell unprotect(this);
         return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset());
     }
 
     js::TryNoteArray *trynotes() {
         JS_ASSERT(hasTrynotes());
+        js::AutoUnprotectCell unprotect(this);
         return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
     }
 
     js::BlockScopeArray *blockScopes() {
         JS_ASSERT(hasBlockScopes());
         return reinterpret_cast<js::BlockScopeArray *>(data + blockScopesOffset());
     }
 
     bool hasLoops();
 
     js::HeapPtrAtom &getAtom(size_t index) const {
+        js::AutoUnprotectCell unprotect(this);
         JS_ASSERT(index < natoms);
         return atoms[index];
     }
 
     js::HeapPtrAtom &getAtom(jsbytecode *pc) const {
         JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
         return getAtom(GET_UINT32_INDEX(pc));
     }
@@ -979,16 +1167,17 @@ class JSScript : public js::gc::Barriere
     }
 
     js::PropertyName *getName(jsbytecode *pc) const {
         JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
         return getAtom(GET_UINT32_INDEX(pc))->asPropertyName();
     }
 
     JSObject *getObject(size_t index) {
+        js::AutoUnprotectCell unprotect(this);
         js::ObjectArray *arr = objects();
         JS_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
     size_t innerObjectsStart() {
         // The first object contains the caller if savedCallerFun is used.
         return savedCallerFun ? 1 : 0;
@@ -1311,16 +1500,17 @@ class LazyScript : public gc::BarrieredC
     bool directlyInsideEval() const {
         return directlyInsideEval_;
     }
     void setDirectlyInsideEval() {
         directlyInsideEval_ = true;
     }
 
     bool usesArgumentsAndApply() const {
+        AutoUnprotectCell unprotect(this);
         return usesArgumentsAndApply_;
     }
     void setUsesArgumentsAndApply() {
         usesArgumentsAndApply_ = true;
     }
 
     bool hasBeenCloned() const {
         return hasBeenCloned_;
@@ -1335,19 +1525,21 @@ class LazyScript : public gc::BarrieredC
     void setTreatAsRunOnce() {
         treatAsRunOnce_ = true;
     }
 
     ScriptSource *source() const {
         return sourceObject()->source();
     }
     uint32_t begin() const {
+        AutoUnprotectCell unprotect(this);
         return begin_;
     }
     uint32_t end() const {
+        AutoUnprotectCell unprotect(this);
         return end_;
     }
     uint32_t lineno() const {
         return lineno_;
     }
     uint32_t column() const {
         return column_;
     }
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -21,19 +21,19 @@ namespace js {
 
 inline
 Bindings::Bindings()
     : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT), numArgs_(0), numVars_(0)
 {}
 
 inline
 AliasedFormalIter::AliasedFormalIter(JSScript *script)
-  : begin_(script->bindings.bindingArray()),
+  : begin_(script->bindingArray()),
     p_(begin_),
-    end_(begin_ + (script->funHasAnyAliasedFormal ? script->bindings.numArgs() : 0)),
+    end_(begin_ + (script->getFunHasAnyAliasedFormal() ? script->numArgs() : 0)),
     slot_(CallObject::RESERVED_SLOTS)
 {
     settle();
 }
 
 inline void
 ScriptCounts::destroy(FreeOp *fop)
 {
@@ -93,16 +93,17 @@ JSScript::getRegExp(size_t index)
 
 inline js::GlobalObject &
 JSScript::global() const
 {
     /*
      * A JSScript always marks its compartment's global (via bindings) so we
      * can assert that maybeGlobal is non-null here.
      */
+    js::AutoUnprotectCell unprotect(this);
     return *compartment()->maybeGlobal();
 }
 
 inline JSPrincipals *
 JSScript::principals()
 {
     return compartment()->principals;
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -4088,25 +4088,34 @@ CompareStringsImpl(JSContext *cx, JSStri
     const jschar *s1 = str1->getChars(cx);
     if (!s1)
         return false;
 
     const jschar *s2 = str2->getChars(cx);
     if (!s2)
         return false;
 
-    return CompareChars(s1, str1->length(), s2, str2->length(), result);
+    *result = CompareChars(s1, str1->length(), s2, str2->length());
+    return true;
 }
 
 bool
 js::CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
 {
     return CompareStringsImpl(cx, str1, str2, result);
 }
 
+int32_t
+js::CompareAtoms(JSAtom *atom1, JSAtom *atom2)
+{
+    AutoUnprotectCell unprotect1(atom1);
+    AutoUnprotectCell unprotect2(atom2);
+    return CompareChars(atom1->chars(), atom1->length(), atom2->chars(), atom2->length());
+}
+
 bool
 js::StringEqualsAscii(JSLinearString *str, const char *asciiBytes)
 {
     size_t length = strlen(asciiBytes);
 #ifdef DEBUG
     for (size_t i = 0; i != length; ++i)
         JS_ASSERT(unsigned(asciiBytes[i]) <= 127);
 #endif
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -43,29 +43,26 @@ SkipSpace(const jschar *s, const jschar 
     while (s < end && unicode::IsSpace(*s))
         s++;
 
     return s;
 }
 
 // Return less than, equal to, or greater than zero depending on whether
 // s1 is less than, equal to, or greater than s2.
-inline bool
-CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result)
+inline int32_t
+CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2)
 {
     size_t n = Min(l1, l2);
     for (size_t i = 0; i < n; i++) {
-        if (int32_t cmp = s1[i] - s2[i]) {
-            *result = cmp;
-            return true;
-        }
+        if (int32_t cmp = s1[i] - s2[i])
+            return cmp;
     }
 
-    *result = (int32_t)(l1 - l2);
-    return true;
+    return (int32_t)(l1 - l2);
 }
 
 }  /* namespace js */
 
 extern JSString * JS_FASTCALL
 js_toLowerCase(JSContext *cx, JSString *str);
 
 extern JSString * JS_FASTCALL
@@ -205,16 +202,19 @@ EqualStrings(JSLinearString *str1, JSLin
 
 /*
  * Return less than, equal to, or greater than zero depending on whether
  * str1 is less than, equal to, or greater than str2.
  */
 extern bool
 CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result);
 
+int32_t
+CompareAtoms(JSAtom *atom1, JSAtom *atom2);
+
 /*
  * Return true if the string matches the given sequence of ASCII bytes.
  */
 extern bool
 StringEqualsAscii(JSLinearString *str, const char *asciiBytes);
 
 /* Return true if the string contains a pattern anywhere inside it. */
 extern bool
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5424,16 +5424,19 @@ ProcessArgs(JSContext *cx, JSObject *obj
              jit::js_IonOptions.rangeAnalysis = false;
          else
              return OptionFailure("ion-range-analysis", str);
      }
 
     if (op->getBoolOption("ion-check-range-analysis"))
         jit::js_IonOptions.checkRangeAnalysis = true;
 
+    if (op->getBoolOption("ion-check-thread-safety"))
+        jit::js_IonOptions.checkThreadSafety = true;
+
     if (const char *str = op->getStringOption("ion-inlining")) {
         if (strcmp(str, "on") == 0)
             jit::js_IonOptions.inlining = true;
         else if (strcmp(str, "off") == 0)
             jit::js_IonOptions.inlining = false;
         else
             return OptionFailure("ion-inlining", str);
     }
@@ -5735,16 +5738,18 @@ main(int argc, char **argv, char **envp)
         || !op.addStringOption('\0', "ion-licm", "on/off",
                                "Loop invariant code motion (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
                                "Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-range-analysis", "on/off",
                                "Range analysis (default: on, off to disable)")
         || !op.addBoolOption('\0', "ion-check-range-analysis",
                                "Range analysis checking")
+        || !op.addBoolOption('\0', "ion-check-thread-safety",
+                             "Builder thread safety checking")
         || !op.addStringOption('\0', "ion-inlining", "on/off",
                                "Inline methods where possible (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-osr", "on/off",
                                "On-Stack Replacement (default: on, off to disable)")
         || !op.addStringOption('\0', "ion-limit-script-size", "on/off",
                                "Don't compile very large scripts (default: on, off to disable)")
         || !op.addIntOption('\0', "ion-uses-before-compile", "COUNT",
                             "Wait for COUNT calls or iterations before compiling "
@@ -5828,18 +5833,25 @@ main(int argc, char **argv, char **envp)
     }
 #endif
 #endif
 
     // Start the engine.
     if (!JS_Init())
         return 1;
 
+    // When doing thread safety checks for VM accesses made during Ion compilation,
+    // we rely on protected memory and only the main thread should be active.
+    JSUseHelperThreads useHelperThreads =
+        op.getBoolOption("ion-check-thread-safety")
+        ? JS_NO_HELPER_THREADS
+        : JS_USE_HELPER_THREADS;
+
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
-    rt = JS_NewRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS);
+    rt = JS_NewRuntime(32L * 1024L * 1024L, useHelperThreads);
     if (!rt)
         return 1;
     gTimeoutFunc = NullValue();
     if (!JS_AddNamedValueRootRT(rt, &gTimeoutFunc, "gTimeoutFunc"))
         return 1;
 
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 #ifdef JSGC_GENERATIONAL
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -148,28 +148,30 @@ class GlobalObject : public JSObject
 
     void setIntrinsicsHolder(JSObject *obj) {
         JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
         setSlot(INTRINSICS, ObjectValue(*obj));
     }
 
   public:
     Value getConstructor(JSProtoKey key) const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(APPLICATION_SLOTS + key);
+        return getSlotRefForCompilation(APPLICATION_SLOTS + key);
     }
 
     void setConstructor(JSProtoKey key, const Value &v) {
         JS_ASSERT(key <= JSProto_LIMIT);
         setSlot(APPLICATION_SLOTS + key, v);
     }
 
     Value getPrototype(JSProtoKey key) const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(APPLICATION_SLOTS + JSProto_LIMIT + key);
+        return getSlotRefForCompilation(APPLICATION_SLOTS + JSProto_LIMIT + key);
     }
 
     void setPrototype(JSProtoKey key, const Value &value) {
         JS_ASSERT(key <= JSProto_LIMIT);
         setSlot(APPLICATION_SLOTS + JSProto_LIMIT + key, value);
     }
 
     static uint32_t constructorPropertySlot(JSProtoKey key) {
@@ -343,16 +345,17 @@ class GlobalObject : public JSObject
             return &getPrototype(JSProto_Array).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitArrayClass(cx, self))
             return nullptr;
         return &self->getPrototype(JSProto_Array).toObject();
     }
 
     JSObject *maybeGetArrayPrototype() {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         if (arrayClassInitialized())
             return &getPrototype(JSProto_Array).toObject();
         return nullptr;
     }
 
     JSObject *getOrCreateBooleanPrototype(JSContext *cx) {
         if (booleanClassInitialized())
             return &getPrototype(JSProto_Boolean).toObject();
@@ -457,16 +460,28 @@ class GlobalObject : public JSObject
         if (v.isObject())
             return &v.toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!init(cx, self))
             return nullptr;
         return &self->getSlot(slot).toObject();
     }
 
+    const HeapSlot &getSlotRefForCompilation(uint32_t slot) const {
+        // This method should only be used for slots that are either eagerly
+        // initialized on creation of the global or only change under the
+        // compilation lock. Note that the dynamic slots pointer for global
+        // objects can only change under the compilation lock.
+        JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(getClass()));
+        uint32_t fixed = numFixedSlotsForCompilation();
+        if (slot < fixed)
+            return fixedSlots()[slot];
+        return slots[slot - fixed];
+    }
+
   public:
     JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator,
                                  initIteratorClasses);
     }
 
     JSObject *getOrCreateArrayIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, ARRAY_ITERATOR_PROTO, initIteratorClasses);
@@ -507,22 +522,28 @@ class GlobalObject : public JSObject
             return &getPrototype(JSProto_DataView).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitTypedArrayClasses(cx, self))
             return nullptr;
         return &self->getPrototype(JSProto_DataView).toObject();
     }
 
     JSObject *intrinsicsHolder() {
-        JS_ASSERT(!getSlotRef(INTRINSICS).isUndefined());
-        return &getSlotRef(INTRINSICS).toObject();
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
+        JS_ASSERT(!getSlotRefForCompilation(INTRINSICS).isUndefined());
+        return &getSlotRefForCompilation(INTRINSICS).toObject();
     }
 
     bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) {
         JSObject *holder = intrinsicsHolder();
+
+        AutoUnprotectCellUnderCompilationLock unprotect0(holder);
+        AutoUnprotectCellUnderCompilationLock unprotect1(holder->lastProperty());
+        AutoUnprotectCellUnderCompilationLock unprotect2(holder->lastProperty()->base());
+
         if (Shape *shape = holder->nativeLookupPure(name)) {
             *vp = holder->getSlot(shape->slot());
             return true;
         }
         return false;
     }
 
     bool getIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue value) {
@@ -545,18 +566,20 @@ class GlobalObject : public JSObject
         RootedValue valCopy(cx, value);
         return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
     }
 
     bool getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
                                unsigned nargs, MutableHandleValue funVal);
 
     RegExpStatics *getRegExpStatics() const {
-        JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
-        return static_cast<RegExpStatics *>(resObj.getPrivate());
+        AutoUnprotectCellUnderCompilationLock unprotect0(this);
+        JSObject &resObj = getSlotRefForCompilation(REGEXP_STATICS).toObject();
+        AutoUnprotectCell unprotect1(&resObj);
+        return static_cast<RegExpStatics *>(resObj.getPrivate(/* nfixed = */ 1));
     }
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
     Value createDataViewForThis() const {
@@ -573,19 +596,20 @@ class GlobalObject : public JSObject
     }
 
     static bool isRuntimeCodeGenEnabled(JSContext *cx, Handle<GlobalObject*> global);
 
     // Warn about use of the deprecated watch/unwatch functions in the global
     // in which |obj| was created, if no prior warning was given.
     static bool warnOnceAboutWatch(JSContext *cx, HandleObject obj);
 
-    const Value &getOriginalEval() const {
-        JS_ASSERT(getSlot(EVAL).isObject());
-        return getSlot(EVAL);
+    Value getOriginalEval() const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
+        JS_ASSERT(getSlotRefForCompilation(EVAL).isObject());
+        return getSlotRefForCompilation(EVAL);
     }
 
     // Implemented in jsiter.cpp.
     static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
@@ -760,12 +784,12 @@ DefinePropertiesAndBrand(JSContext *cx, 
 typedef HashSet<GlobalObject *, DefaultHasher<GlobalObject *>, SystemAllocPolicy> GlobalObjectSet;
 
 } // namespace js
 
 template<>
 inline bool
 JSObject::is<js::GlobalObject>() const
 {
-    return !!(js::GetObjectClass(const_cast<JSObject*>(this))->flags & JSCLASS_IS_GLOBAL);
+    return !!(getClass()->flags & JSCLASS_IS_GLOBAL);
 }
 
 #endif /* vm_GlobalObject_h */
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -329,16 +329,35 @@ js::ObjectImpl::nativeLookup(ExclusiveCo
 
 Shape *
 js::ObjectImpl::nativeLookupPure(jsid id)
 {
     MOZ_ASSERT(isNative());
     return Shape::searchNoHashify(lastProperty(), id);
 }
 
+uint32_t
+js::ObjectImpl::numFixedSlotsForCompilation() const
+{
+    // This is an alternative method for getting the number of fixed slots
+    // in an object. It requires more logic and memory accesses than
+    // numFixedSlots() but is safe to be called from the compilation thread,
+    // even if the main thread is actively mutating the VM.
+    if (static_cast<const JSObject *>(this)->is<ArrayObject>())
+        return 0;
+#ifdef JSGC_GENERATIONAL
+    // The compiler does not have access to nursery things, so if this object
+    // is in the nursery we can fall back to numFixedSlots().
+    if (!isTenured())
+        return numFixedSlots();
+#endif
+    gc::AllocKind kind = tenuredGetAllocKind();
+    return gc::GetGCKindSlots(kind, getClass());
+}
+
 void
 js::ObjectImpl::markChildren(JSTracer *trc)
 {
     MarkTypeObject(trc, &type_, "type");
 
     MarkShape(trc, &shape_, "shape");
 
     const Class *clasp = type_->clasp;
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -975,21 +975,29 @@ class ObjectImpl : public gc::BarrieredC
     const JSObject * asObjectPtr() const { return reinterpret_cast<const JSObject *>(this); }
 
     friend inline Value ObjectValue(ObjectImpl &obj);
 
     /* These functions are public, and they should remain public. */
 
   public:
     JSObject * getProto() const {
-        return type_->proto;
+        AutoUnprotectCellUnderCompilationLock unprotect0(this);
+        return type_->getProto();
     }
 
     const Class *getClass() const {
-        return type_->clasp;
+        AutoUnprotectCellUnderCompilationLock unprotect0(this);
+        return type_->getClass();
+    }
+
+    const Class *getClassImmutable() {
+        // For use on objects which cannot be modified after construction.
+        AutoUnprotectCell unprotect(this);
+        return type_->getClass();
     }
 
     static inline bool
     isExtensible(ExclusiveContext *cx, Handle<ObjectImpl*> obj, bool *extensible);
 
     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
     // This method really shouldn't exist -- but there are a few internal
     // places that want it (JITs and the like), and it'd be a pain to mark them
@@ -1022,20 +1030,28 @@ class ObjectImpl : public gc::BarrieredC
     bool containsDenseElement(uint32_t idx) {
         JS_ASSERT(isNative());
         return idx < getDenseInitializedLength() && !elements[idx].isMagic(JS_ELEMENTS_HOLE);
     }
     uint32_t getDenseInitializedLength() {
         JS_ASSERT(isNative());
         return getElementsHeader()->initializedLength;
     }
+    uint32_t getDenseInitializedLengthForCompilation() {
+        // Note: isNative() generally can't be safely called off thread.
+        return getElementsHeader()->initializedLength;
+    }
     uint32_t getDenseCapacity() {
         JS_ASSERT(isNative());
         return getElementsHeader()->capacity;
     }
+    uint32_t getDenseCapacityForCompilation() {
+        // Note: isNative() generally can't be safely called off thread.
+        return getElementsHeader()->capacity;
+    }
 
     bool makeElementsSparse(JSContext *cx) {
         JS_NEW_OBJECT_REPRESENTATION_ONLY();
         MOZ_ASSUME_UNREACHABLE("NYI");
     }
 
   protected:
 #ifdef DEBUG
@@ -1189,34 +1205,44 @@ class ObjectImpl : public gc::BarrieredC
     }
 
     bool isNative() const {
         return lastProperty()->isNative();
     }
 
     types::TypeObject *type() const {
         MOZ_ASSERT(!hasLazyType());
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return type_;
     }
 
     uint32_t numFixedSlots() const {
         return reinterpret_cast<const shadow::Object *>(this)->numFixedSlots();
     }
 
+    uint32_t numFixedSlotsForCompilation() const;
+
     /*
      * Whether this is the only object which has its specified type. This
      * object will have its type constructed lazily as needed by analysis.
      */
-    bool hasSingletonType() const { return !!type_->singleton; }
+    bool hasSingletonType() const {
+        AutoUnprotectCellUnderCompilationLock unprotect0(this);
+        AutoUnprotectCellUnderCompilationLock unprotect1(type_);
+        return !!type_->singleton;
+    }
 
     /*
      * Whether the object's type has not been constructed yet. If an object
      * might have a lazy type, use getType() below, otherwise type().
      */
-    bool hasLazyType() const { return type_->lazy(); }
+    bool hasLazyType() const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
+        return type_->lazy();
+    }
 
     uint32_t slotSpan() const {
         if (inDictionaryMode())
             return lastProperty()->base()->slotSpan();
         return lastProperty()->slotSpan();
     }
 
     /* Compute dynamicSlotsCount() for this object. */
@@ -1363,17 +1389,17 @@ class ObjectImpl : public gc::BarrieredC
     /* For slots which are known to always be fixed, due to the way they are allocated. */
 
     HeapSlot &getFixedSlotRef(uint32_t slot) {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
     const Value &getFixedSlot(uint32_t slot) const {
-        MOZ_ASSERT(slot < numFixedSlots());
+        MOZ_ASSERT(slot < numFixedSlotsForCompilation());
         return fixedSlots()[slot];
     }
 
     void setFixedSlot(uint32_t slot, const Value &value) {
         MOZ_ASSERT(slot < numFixedSlots());
         fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value);
     }
 
@@ -1460,17 +1486,17 @@ class ObjectImpl : public gc::BarrieredC
     /* Private data accessors. */
 
     inline void *&privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
         /*
          * The private pointer of an object can hold any word sized value.
          * Private pointers are stored immediately after the last fixed slot of
          * the object.
          */
-        MOZ_ASSERT(nfixed == numFixedSlots());
+        MOZ_ASSERT(nfixed == numFixedSlotsForCompilation());
         MOZ_ASSERT(hasPrivate());
         HeapSlot *end = &fixedSlots()[nfixed];
         return *reinterpret_cast<void**>(end);
     }
 
     bool hasPrivate() const {
         return getClass()->hasPrivate();
     }
@@ -1534,16 +1560,20 @@ BarrieredCell<ObjectImpl>::zone() const
     return zone;
 }
 
 template <>
 JS_ALWAYS_INLINE Zone *
 BarrieredCell<ObjectImpl>::zoneFromAnyThread() const
 {
     const ObjectImpl* obj = static_cast<const ObjectImpl*>(this);
+
+    // If accesses to this object are permitted then so are accesses to its zone.
+    AutoUnprotectCell unprotect(obj->shape_);
+
     return obj->shape_->zoneFromAnyThread();
 }
 
 // TypeScript::global uses 0x1 as a special value.
 template<>
 /* static */ inline bool
 BarrieredCell<ObjectImpl>::isNullLike(ObjectImpl *obj)
 {
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -407,20 +407,20 @@ class RegExpObject : public JSObject
     void setMultiline(bool enabled) {
         setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled));
     }
 
     void setSticky(bool enabled) {
         setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled));
     }
 
-    bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
-    bool global() const     { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
-    bool multiline() const  { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
-    bool sticky() const     { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
+    bool ignoreCase() const { return getFixedSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
+    bool global() const     { return getFixedSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
+    bool multiline() const  { return getFixedSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
+    bool sticky() const     { return getFixedSlot(STICKY_FLAG_SLOT).toBoolean(); }
 
     void shared(RegExpGuard *g) const {
         JS_ASSERT(maybeShared() != nullptr);
         g->init(*maybeShared());
     }
 
     bool getShared(ExclusiveContext *cx, RegExpGuard *g) {
         if (RegExpShared *shared = maybeShared()) {
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -42,17 +42,22 @@ NewObjectCache::newObjectFromHit(JSConte
 {
     // The new object cache does not account for metadata attached via callbacks.
     JS_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
 
     JS_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
     Entry *entry = &entries[entry_];
 
     JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
-    if (templateObj->type()->isLongLivedForCachedAlloc())
+
+    // Do an end run around JSObject::type() to avoid doing AutoUnprotectCell
+    // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread.
+    types::TypeObject *type = templateObj->type_;
+
+    if (type->isLongLivedForCachedAlloc())
         heap = gc::TenuredHeap;
 
     JSObject *obj = js_NewGCObject<NoGC>(cx, entry->kind, heap);
     if (obj) {
         copyCachedToObject(obj, templateObj, entry->kind);
         probes::CreateObject(cx, obj);
         return obj;
     }
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -10,16 +10,20 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Util.h"
 
 #include <locale.h>
 #include <string.h>
 
+#if defined(DEBUG) && !defined(XP_WIN)
+# include <sys/mman.h>
+#endif
+
 #include "jsatom.h"
 #include "jsdtoa.h"
 #include "jsgc.h"
 #include "jsmath.h"
 #include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jswatchpoint.h"
@@ -257,16 +261,17 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     structuredCloneCallbacks(nullptr),
     telemetryCallback(nullptr),
     propertyRemovals(0),
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
+    heapProtected_(false),
     mathCache_(nullptr),
     activeCompilations_(0),
     keepAtoms_(0),
     trustedPrincipals_(nullptr),
     atomsCompartment_(nullptr),
     beingDestroyed_(false),
     wrapObjectCallback(TransparentObjectWrapper),
     sameCompartmentWrapObjectCallback(nullptr),
@@ -783,16 +788,86 @@ JSRuntime::onOutOfMemory(void *p, size_t
 
 bool
 JSRuntime::activeGCInAtomsZone()
 {
     Zone *zone = atomsCompartment_->zone();
     return zone->needsBarrier() || zone->isGCScheduled() || zone->wasGCStarted();
 }
 
+#if defined(DEBUG) && !defined(XP_WIN)
+
+AutoProtectHeapForCompilation::AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : runtime(rt)
+{
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+    JS_ASSERT(!runtime->heapProtected_);
+    runtime->heapProtected_ = true;
+
+    for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) {
+        Chunk *chunk = r.front();
+        // Note: Don't protect the last page in the chunk, which stores
+        // immutable info and needs to be accessible for runtimeFromAnyThread()
+        // in AutoUnprotectCell.
+        if (mprotect(chunk, ChunkSize - sizeof(Arena), PROT_NONE))
+            MOZ_CRASH();
+    }
+}
+
+AutoProtectHeapForCompilation::~AutoProtectHeapForCompilation()
+{
+    JS_ASSERT(runtime->heapProtected_);
+    JS_ASSERT(runtime->unprotectedArenas.empty());
+    runtime->heapProtected_ = false;
+
+    for (GCChunkSet::Range r(runtime->gcChunkSet.all()); !r.empty(); r.popFront()) {
+        Chunk *chunk = r.front();
+        if (mprotect(chunk, ChunkSize - sizeof(Arena), PROT_READ | PROT_WRITE))
+            MOZ_CRASH();
+    }
+}
+
+AutoUnprotectCell::AutoUnprotectCell(const Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : runtime(cell->runtimeFromAnyThread()), arena(nullptr)
+{
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+    if (!runtime->heapProtected_)
+        return;
+
+    ArenaHeader *base = cell->arenaHeader();
+    for (size_t i = 0; i < runtime->unprotectedArenas.length(); i++) {
+        if (base == runtime->unprotectedArenas[i])
+            return;
+    }
+
+    arena = base;
+
+    if (mprotect(arena, sizeof(Arena), PROT_READ | PROT_WRITE))
+        MOZ_CRASH();
+
+    if (!runtime->unprotectedArenas.append(arena))
+        MOZ_CRASH();
+}
+
+AutoUnprotectCell::~AutoUnprotectCell()
+{
+    if (!arena)
+        return;
+
+    if (mprotect(arena, sizeof(Arena), PROT_NONE))
+        MOZ_CRASH();
+
+    JS_ASSERT(arena == runtime->unprotectedArenas.back());
+    runtime->unprotectedArenas.popBack();
+}
+
+#endif // DEBUG && !XP_WIN
+
 #ifdef JS_WORKER_THREADS
 
 void
 JSRuntime::setUsedByExclusiveThread(Zone *zone)
 {
     JS_ASSERT(!zone->usedByExclusiveThread);
     zone->usedByExclusiveThread = true;
     numExclusiveThreads++;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -672,16 +672,17 @@ struct MallocProvider
 
 namespace gc {
 class MarkingValidator;
 } // namespace gc
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 class AutoLockForExclusiveAccess;
+class AutoProtectHeapForCompilation;
 
 void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
 
 } // namespace js
 
 struct JSRuntime : public JS::shadow::Runtime,
                    public js::MallocProvider<JSRuntime>
 {
@@ -1426,16 +1427,29 @@ struct JSRuntime : public JS::shadow::Ru
 #if !EXPOSE_INTL_API
     /* Number localization, used by jsnum.cpp. */
     const char          *thousandsSeparator;
     const char          *decimalSeparator;
     const char          *numGrouping;
 #endif
 
   private:
+    friend class js::AutoProtectHeapForCompilation;
+    friend class js::AutoUnprotectCell;
+    mozilla::DebugOnly<bool> heapProtected_;
+#ifdef DEBUG
+    js::Vector<js::gc::ArenaHeader *, 0, js::SystemAllocPolicy> unprotectedArenas;
+
+  public:
+    bool heapProtected() {
+        return heapProtected_;
+    }
+#endif
+
+  private:
     js::MathCache *mathCache_;
     js::MathCache *createMathCache(JSContext *cx);
   public:
     js::MathCache *getMathCache(JSContext *cx) {
         return mathCache_ ? mathCache_ : createMathCache(cx);
     }
     js::MathCache *maybeGetMathCache() {
         return mathCache_;
@@ -2011,16 +2025,33 @@ class RuntimeAllocPolicy
     void *calloc_(size_t bytes) { return runtime->calloc_(bytes); }
     void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
+class AutoProtectHeapForCompilation
+{
+  public:
+#if defined(DEBUG) && !defined(XP_WIN)
+    JSRuntime *runtime;
+
+    AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+    ~AutoProtectHeapForCompilation();
+#else
+    AutoProtectHeapForCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+#endif
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 #endif /* vm_Runtime_h */
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -69,19 +69,22 @@ StaticScopeIter<allowGC>::hasDynamicScop
 }
 
 template <AllowGC allowGC>
 inline Shape *
 StaticScopeIter<allowGC>::scopeShape() const
 {
     JS_ASSERT(hasDynamicScopeObject());
     JS_ASSERT(type() != NAMED_LAMBDA);
-    return type() == BLOCK
-           ? block().lastProperty()
-           : funScript()->bindings.callObjShape();
+    if (type() == BLOCK) {
+        AutoUnprotectCell unprotect(&block());
+        return block().lastProperty();
+    }
+    AutoUnprotectCell unprotect(funScript());
+    return funScript()->bindings.callObjShape();
 }
 
 template <AllowGC allowGC>
 inline typename StaticScopeIter<allowGC>::Type
 StaticScopeIter<allowGC>::type() const
 {
     if (onNamedLambda)
         return NAMED_LAMBDA;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -30,17 +30,17 @@ using mozilla::PodZero;
 
 typedef Rooted<ArgumentsObject *> RootedArgumentsObject;
 
 /*****************************************************************************/
 
 static JSObject *
 InnermostStaticScope(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(pc >= script->code && pc < script->code + script->length);
+    JS_ASSERT(pc >= script->getCode() && pc < script->getCode() + script->getLength());
     JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
 
     uint32_t blockIndex = GET_UINT32_INDEX(pc + 2 * sizeof(uint16_t));
 
     if (blockIndex == UINT32_MAX)
         return script->function();
     return &script->getObject(blockIndex)->as<StaticBlockObject>();
 }
@@ -63,17 +63,17 @@ js::ScopeCoordinateToStaticScopeShape(JS
 
 PropertyName *
 js::ScopeCoordinateName(JSScript *script, jsbytecode *pc)
 {
     Shape::Range<NoGC> r(ScopeCoordinateToStaticScopeShape(script, pc));
     ScopeCoordinate sc(pc);
     while (r.front().slot() != sc.slot)
         r.popFront();
-    jsid id = r.front().propid();
+    jsid id = r.front().propidRaw();
 
     /* Beware nameless destructuring formal. */
     if (!JSID_IS_ATOM(id))
         return script->runtimeFromAnyThread()->atomState.empty;
     return JSID_TO_ATOM(id)->asPropertyName();
 }
 
 JSScript *
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -176,17 +176,18 @@ class ScopeObject : public JSObject
 
   public:
     /*
      * Since every scope chain terminates with a global object and GlobalObject
      * does not derive ScopeObject (it has a completely different layout), the
      * enclosing scope of a ScopeObject is necessarily non-null.
      */
     inline JSObject &enclosingScope() const {
-        return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
+        AutoUnprotectCell unprotect(this);
+        return getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
     }
 
     void setEnclosingScope(HandleObject obj);
 
     /*
      * Get or set an aliased variable contained in this scope. Unaliased
      * variables should instead access the StackFrame. Aliased variable access
      * is primarily made through JOF_SCOPECOORD ops which is why these members
@@ -227,28 +228,30 @@ class CallObject : public ScopeObject
 
     static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);
 
     static CallObject *createForFunction(JSContext *cx, AbstractFramePtr frame);
     static CallObject *createForStrictEval(JSContext *cx, AbstractFramePtr frame);
 
     /* True if this is for a strict mode eval frame. */
     bool isForEval() const {
-        JS_ASSERT(getReservedSlot(CALLEE_SLOT).isObjectOrNull());
-        JS_ASSERT_IF(getReservedSlot(CALLEE_SLOT).isObject(),
-                     getReservedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
-        return getReservedSlot(CALLEE_SLOT).isNull();
+        AutoUnprotectCell unprotect(this);
+        JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
+        JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
+                     getFixedSlot(CALLEE_SLOT).toObject().is<JSFunction>());
+        return getFixedSlot(CALLEE_SLOT).isNull();
     }
 
     /*
      * Returns the function for which this CallObject was created. (This may
      * only be called if !isForEval.)
      */
     JSFunction &callee() const {
-        return getReservedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
+        AutoUnprotectCell unprotect(this);
+        return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
     }
 
     /* Get/set the aliased variable referred to by 'bi'. */
     const Value &aliasedVar(AliasedFormalIter fi) {
         return getSlot(fi.scopeSlot());
     }
 
     inline void setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v);
@@ -328,17 +331,17 @@ class BlockObject : public NestedScopeOb
   public:
     static const unsigned RESERVED_SLOTS = 2;
     static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT4_BACKGROUND;
 
     static const Class class_;
 
     /* Return the number of variables associated with this block. */
     uint32_t slotCount() const {
-        return propertyCount();
+        return propertyCountForCompilation();
     }
 
     /*
      * Return the local corresponding to the ith binding where i is in the
      * range [0, slotCount()) and the return local index is in the range
      * [script->nfixed, script->nfixed + script->nslots).
      */
     unsigned slotToLocalIndex(const Bindings &bindings, unsigned slot) {
@@ -363,17 +366,18 @@ class BlockObject : public NestedScopeOb
 
 class StaticBlockObject : public BlockObject
 {
   public:
     static StaticBlockObject *create(ExclusiveContext *cx);
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
-        return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
+        AutoUnprotectCell unprotect(this);
+        return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
     }
 
     /*
      * A refinement of enclosingStaticScope that returns nullptr if the enclosing
      * static scope is a JSFunction.
      */
     inline StaticBlockObject *enclosingBlock() const;
 
@@ -393,17 +397,20 @@ class StaticBlockObject : public BlockOb
         return slotValue(i).isTrue();
     }
 
     /*
      * A static block object is cloned (when entering the block) iff some
      * variable of the block isAliased.
      */
     bool needsClone() {
-        return !slotValue(0).isFalse();
+        // The first variable slot will always indicate whether the object has
+        // any aliased vars. Bypass slotValue() to allow testing this off thread.
+        AutoUnprotectCell unprotect(this);
+        return !getFixedSlot(RESERVED_SLOTS).isFalse();
     }
 
     /* Frontend-only functions ***********************************************/
 
     /* Initialization functions for above fields. */
     void setAliased(unsigned i, bool aliased) {
         JS_ASSERT_IF(i > 0, slotValue(i-1).isBoolean());
         setSlotValue(i, BooleanValue(aliased));
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -189,17 +189,17 @@ ShapeTable::search(jsid id, bool adding)
 
     /* Miss: return space for a new entry. */
     stored = *spp;
     if (SHAPE_IS_FREE(stored))
         return spp;
 
     /* Hit: return entry. */
     shape = SHAPE_CLEAR_COLLISION(stored);
-    if (shape && shape->propid() == id)
+    if (shape && shape->propidRaw() == id)
         return spp;
 
     /* Collision: double hash. */
     sizeLog2 = HASH_BITS - hashShift;
     hash2 = HASH2(hash0, sizeLog2, hashShift);
     sizeMask = JS_BITMASK(sizeLog2);
 
 #ifdef DEBUG
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1028,21 +1028,25 @@ class Shape : public gc::BarrieredCell<S
 
         Shape &front() const {
             JS_ASSERT(!empty());
             return *cursor;
         }
 
         void popFront() {
             JS_ASSERT(!empty());
+            AutoUnprotectCell unprotect(cursor);
             cursor = cursor->parent;
         }
     };
 
-    const Class *getObjectClass() const { return base()->clasp; }
+    const Class *getObjectClass() const {
+        AutoUnprotectCell unprotect(base());
+        return base()->clasp;
+    }
     JSObject *getObjectParent() const { return base()->parent; }
     JSObject *getObjectMetadata() const { return base()->metadata; }
 
     static Shape *setObjectParent(ExclusiveContext *cx,
                                   JSObject *obj, TaggedProto proto, Shape *last);
     static Shape *setObjectMetadata(JSContext *cx,
                                     JSObject *metadata, TaggedProto proto, Shape *last);
     static Shape *setObjectFlag(ExclusiveContext *cx,
@@ -1089,22 +1093,25 @@ class Shape : public gc::BarrieredCell<S
 
   public:
     /* Public bits stored in shape->flags. */
     enum {
         HAS_SHORTID     = 0x40,
         PUBLIC_FLAGS    = HAS_SHORTID
     };
 
-    bool inDictionary() const   { return (flags & IN_DICTIONARY) != 0; }
+    bool inDictionary() const {
+        AutoUnprotectCell unprotect(this);
+        return (flags & IN_DICTIONARY) != 0;
+    }
     unsigned getFlags() const  { return flags & PUBLIC_FLAGS; }
     bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
 
     PropertyOp getter() const { return base()->rawGetter; }
-    bool hasDefaultGetter() const  { return !base()->rawGetter; }
+    bool hasDefaultGetter() const { return !base()->rawGetter; }
     PropertyOp getterOp() const { JS_ASSERT(!hasGetterValue()); return base()->rawGetter; }
     JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return base()->getterObj; }
 
     // Per ES5, decode null getterObj as the undefined value, which encodes as null.
     Value getterValue() const {
         JS_ASSERT(hasGetterValue());
         return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue();
     }
@@ -1152,38 +1159,54 @@ class Shape : public gc::BarrieredCell<S
                shortid_ == ashortid;
     }
 
     bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
     bool set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp);
 
     BaseShape *base() const { return base_.get(); }
 
-    bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
+    bool hasSlot() const {
+        AutoUnprotectCell unprotect(this);
+        return (attrs & JSPROP_SHARED) == 0;
+    }
     uint32_t slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
-    uint32_t maybeSlot() const { return slotInfo & SLOT_MASK; }
+    uint32_t maybeSlot() const {
+        // Note: Reading a shape's slot off thread can race against main thread
+        // updates to the number of linear searches on the shape, which is
+        // stored in the same slotInfo field. We tolerate this.
+        AutoUnprotectCell unprotect(this);
+        return slotInfo & SLOT_MASK;
+    }
 
     bool isEmptyShape() const {
+        AutoUnprotectCell unprotect(this);
         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
         return JSID_IS_EMPTY(propid_);
     }
 
-    uint32_t slotSpan() const {
+    uint32_t slotSpan(const Class *clasp) const {
         JS_ASSERT(!inDictionary());
-        uint32_t free = JSSLOT_FREE(getObjectClass());
+        uint32_t free = JSSLOT_FREE(clasp);
         return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
     }
 
+    uint32_t slotSpan() const {
+        return slotSpan(getObjectClass());
+    }
+
     void setSlot(uint32_t slot) {
         JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
         slotInfo = slotInfo & ~Shape::SLOT_MASK;
         slotInfo = slotInfo | slot;
     }
 
     uint32_t numFixedSlots() const {
+        // Note: The same race applies here as in maybeSlot().
+        AutoUnprotectCell unprotect(this);
         return (slotInfo >> FIXED_SLOTS_SHIFT);
     }
 
     void setNumFixedSlots(uint32_t nfixed) {
         JS_ASSERT(nfixed < FIXED_SLOTS_MAX);
         slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
         slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
     }
@@ -1195,36 +1218,43 @@ class Shape : public gc::BarrieredCell<S
     void incrementNumLinearSearches() {
         uint32_t count = numLinearSearches();
         JS_ASSERT(count < LINEAR_SEARCHES_MAX);
         slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
         slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
     }
 
     const EncapsulatedId &propid() const {
+        AutoUnprotectCell unprotect(this);
         JS_ASSERT(!isEmptyShape());
         JS_ASSERT(!JSID_IS_VOID(propid_));
         return propid_;
     }
     EncapsulatedId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
 
+    jsid propidRaw() const {
+        AutoUnprotectCell unprotect(this);
+        return propid();
+    }
+
     int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
     int16_t maybeShortid() const { return shortid_; }
 
     /*
      * If SHORTID is set in shape->flags, we use shape->shortid rather
      * than id when calling shape's getter or setter.
      */
     inline bool getUserId(JSContext *cx, MutableHandleId idp) const;
 
     uint8_t attributes() const { return attrs; }
     bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
     bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
     bool writable() const {
         // JS_ASSERT(isDataDescriptor());
+        AutoUnprotectCell unprotect(this);
         return (attrs & JSPROP_READONLY) == 0;
     }
     bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
     bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
 
     bool isDataDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
     }
@@ -1252,23 +1282,37 @@ class Shape : public gc::BarrieredCell<S
         JS_ASSERT_IF(isDataDescriptor(), writable());
         return hasSlot() || (attrs & JSPROP_SHADOWABLE);
     }
 
     uint32_t entryCount() {
         if (hasTable())
             return table().entryCount;
 
-        Shape *shape = this;
         uint32_t count = 0;
-        for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront())
+        for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
             ++count;
         return count;
     }
 
+    uint32_t entryCountForCompilation() {
+        JS_ASSERT(!inDictionary());
+
+        uint32_t count = 0;
+
+        for (Shape *shape = this; shape; ) {
+            AutoUnprotectCell unprotect(shape);
+            if (!shape->isEmptyShape())
+                ++count;
+            shape = shape->parent;
+        }
+
+        return count;
+    }
+
     bool isBigEnoughForAShapeTable() {
         JS_ASSERT(!hasTable());
         Shape *shape = this;
         uint32_t count = 0;
         for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
             ++count;
             if (count >= ShapeTable::MIN_ENTRIES)
                 return true;
@@ -1589,19 +1633,21 @@ Shape::searchLinear(jsid id)
     /*
      * Non-dictionary shapes can acquire a table at any point the main thread
      * is operating on it, so other threads inspecting such shapes can't use
      * their table without racing. This function can be called from any thread
      * on any non-dictionary shape.
      */
     JS_ASSERT(!inDictionary());
 
-    for (Shape *shape = this; shape; shape = shape->parent) {
+    for (Shape *shape = this; shape; ) {
+        AutoUnprotectCell unprotect(shape);
         if (shape->propidRef() == id)
             return shape;
+        shape = shape->parent;
     }
 
     return nullptr;
 }
 
 /*
  * Keep this function in sync with search. It neither hashifies the start
  * shape nor increments linear search count.
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -389,16 +389,17 @@ class JSString : public js::gc::Barriere
 
     JS_ALWAYS_INLINE
     bool isAtom() const {
         return (d.lengthAndFlags & ATOM_BIT);
     }
 
     JS_ALWAYS_INLINE
     JSAtom &asAtom() const {
+        js::AutoUnprotectCell unprotect(this);
         JS_ASSERT(isAtom());
         return *(JSAtom *)this;
     }
 
     /* Only called by the GC for dependent or undepended strings. */
 
     inline bool hasBase() const {
         JS_STATIC_ASSERT((DEPENDENT_FLAGS | JS_BIT(1)) == UNDEPENDED_FLAGS);
@@ -1124,16 +1125,17 @@ JSString::base() const
     JS_ASSERT(hasBase());
     JS_ASSERT(!d.s.u2.base->isInline());
     return d.s.u2.base;
 }
 
 inline js::PropertyName *
 JSAtom::asPropertyName()
 {
+    js::AutoUnprotectCell unprotect(this);
 #ifdef DEBUG
     uint32_t dummy;
     JS_ASSERT(!isIndex(&dummy));
 #endif
     return static_cast<js::PropertyName *>(this);
 }
 
 #endif /* vm_String_h */
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -321,19 +321,21 @@ class TypedArrayObject : public ArrayBuf
 
     static Value bufferValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BUFFER_SLOT);
     }
     static Value byteOffsetValue(TypedArrayObject *tarr) {
         return tarr->getFixedSlot(BYTEOFFSET_SLOT);
     }
     static Value byteLengthValue(TypedArrayObject *tarr) {
+        AutoUnprotectCellUnderCompilationLock unprotect(tarr);
         return tarr->getFixedSlot(BYTELENGTH_SLOT);
     }
     static Value lengthValue(TypedArrayObject *tarr) {
+        AutoUnprotectCellUnderCompilationLock unprotect(tarr);
         return tarr->getFixedSlot(LENGTH_SLOT);
     }
 
     ArrayBufferObject *buffer() const {
         return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<ArrayBufferObject>();
     }
     uint32_t byteOffset() const {
         return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
@@ -341,19 +343,21 @@ class TypedArrayObject : public ArrayBuf
     uint32_t byteLength() const {
         return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
     }
     uint32_t length() const {
         return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
     }
 
     uint32_t type() const {
+        AutoUnprotectCell unprotect(this);
         return getFixedSlot(TYPE_SLOT).toInt32();
     }
     void *viewData() const {
+        AutoUnprotectCellUnderCompilationLock unprotect(this);
         return static_cast<void*>(getPrivate(DATA_SLOT));
     }
 
     inline bool isArrayIndex(jsid id, uint32_t *ip = nullptr);
     void copyTypedArrayElement(uint32_t index, MutableHandleValue vp);
 
     void neuter();