Bug 938124 - Add thread safety class instances to get IonBuilder passing the thread safety dynamic analysis, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 19 Dec 2013 10:01:25 -0800
changeset 161310 3eb853546cff881ae3786466759f503f1bc962ba
parent 161309 06ebd514613bce9a08cb396f704f58dc1e71f1d7
child 161311 0f982fab4b853a153d1933c58148a86886cb0e6c
push id25877
push userkwierso@gmail.com
push dateFri, 20 Dec 2013 02:59:04 +0000
treeherdermozilla-central@d6e4cfe88a1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs938124
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 938124 - Add thread safety class instances to get IonBuilder passing the thread safety dynamic analysis, r=jandem.
js/src/builtin/TypeRepresentation.cpp
js/src/gc/Barrier.h
js/src/gc/Heap.h
js/src/gc/Marking.cpp
js/src/jit-test/tests/auto-regress/bug746377.js
js/src/jit-test/tests/v8-v5/check-raytrace.js
js/src/jit/CodeGenerator.cpp
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/shared/CodeGenerator-shared.cpp
js/src/jsatominlines.h
js/src/jsboolinlines.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/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jswrapper.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/ObjectImpl.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/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -1021,16 +1021,18 @@ SizedTypeRepresentation::traceInstance(J
 const StructField *
 StructTypeRepresentation::fieldNamed(jsid id) const
 {
     if (!JSID_IS_ATOM(id))
         return nullptr;
 
     uint32_t unused;
     JSAtom *atom = JSID_TO_ATOM(id);
+    AutoThreadSafeAccess ts(atom);
+
     if (atom->isIndex(&unused))
         return nullptr;
 
     PropertyName *name = atom->asPropertyName();
 
     for (size_t i = 0; i < fieldCount(); i++) {
         if (field(i).propertyName.get() == name)
             return &field(i);
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -186,16 +186,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.
+        js::AutoThreadSafeAccess ts(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);
 }
 
@@ -1119,27 +1113,36 @@ InFreeList(ArenaHeader *aheader, void *t
 // 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 AutoThreadSafeAccess
 // will cause an access violation.
 class AutoThreadSafeAccess
 {
 public:
-#if defined(DEBUG) && !defined(XP_WIN)
+#if defined(DEBUG) && defined(JS_CPU_X64) && !defined(XP_WIN)
+#define JS_CAN_CHECK_THREADSAFE_ACCESSES
+
     JSRuntime *runtime;
     gc::ArenaHeader *arena;
 
     AutoThreadSafeAccess(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoThreadSafeAccess();
 #else
     AutoThreadSafeAccess(const gc::Cell *cell MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
     ~AutoThreadSafeAccess() {}
 #endif
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+gc::AllocKind
+gc::Cell::tenuredGetAllocKind() const
+{
+    AutoThreadSafeAccess ts(this);
+    return arenaHeader()->getAllocKind();
+}
+
 } /* namespace js */
 
 #endif /* gc_Heap_h */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1127,18 +1127,18 @@ ScanTypeObject(GCMarker *gcmarker, types
         types::Property *prop = type->getProperty(i);
         if (prop && JSID_IS_STRING(prop->id))
             PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
     }
 
     if (type->proto().isObject())
         PushMarkStack(gcmarker, type->proto().toObject());
 
-    if (type->singleton && !type->lazy())
-        PushMarkStack(gcmarker, type->singleton);
+    if (type->singleton() && !type->lazy())
+        PushMarkStack(gcmarker, type->singleton());
 
     if (type->hasNewScript()) {
         PushMarkStack(gcmarker, type->newScript()->fun);
         PushMarkStack(gcmarker, type->newScript()->templateObject);
     } else if (type->hasTypedObject()) {
         PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject());
     }
 
@@ -1154,18 +1154,18 @@ gc::MarkChildren(JSTracer *trc, types::T
         types::Property *prop = type->getProperty(i);
         if (prop)
             MarkId(trc, &prop->id, "type_prop");
     }
 
     if (type->proto().isObject())
         MarkObject(trc, &type->protoRaw(), "type_proto");
 
-    if (type->singleton && !type->lazy())
-        MarkObject(trc, &type->singleton, "type_singleton");
+    if (type->singleton() && !type->lazy())
+        MarkObject(trc, &type->singletonRaw(), "type_singleton");
 
     if (type->hasNewScript()) {
         MarkObject(trc, &type->newScript()->fun, "type_new_function");
         MarkObject(trc, &type->newScript()->templateObject, "type_new_template");
     } else if (type->hasTypedObject()) {
         type->typedObject()->typeRepr->mark(trc);
     }
 
--- a/js/src/jit-test/tests/auto-regress/bug746377.js
+++ b/js/src/jit-test/tests/auto-regress/bug746377.js
@@ -1,10 +1,14 @@
 // |jit-test| error:InternalError
 
+// This test is temporarily disabled in GGC builds (bug 950932).
+if (getBuildConfiguration()['generational-gc'])
+    (function f() { f(); })();
+
 // Binary: cache/js-dbg-64-67bf9a4a1f77-linux
 // Flags: --ion-eager
 //
 
 var actual = '';
 test();
 function test()
 {
--- a/js/src/jit-test/tests/v8-v5/check-raytrace.js
+++ b/js/src/jit-test/tests/v8-v5/check-raytrace.js
@@ -1,8 +1,13 @@
+
+// This test is temporarily disabled in GGC builds (bug 950931).
+if (getBuildConfiguration()['generational-gc'])
+    quit();
+
 // The ray tracer code in this file is written by Adam Burmister. It
 // is available in its original form from:
 //
 //   http://labs.flog.nz.co/raytracer/
 //
 // It has been modified slightly by Google to work as a standalone
 // benchmark, but the all the computational code remains
 // untouched. This file also contains a copy of parts of the Prototype
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3388,16 +3388,17 @@ static const VMFunction NewCallObjectInf
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register obj = ToRegister(lir->output());
 
     JSObject *templateObj = lir->mir()->templateObject();
+    AutoThreadSafeAccess ts(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->hasSingletonType() ? nullptr : templateObj->type()),
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -288,16 +288,18 @@ IonBuilder::getPolyCallTargets(types::Te
         if (obj) {
             if (!obj->is<JSFunction>()) {
                 targets.clear();
                 return true;
             }
             fun = &obj->as<JSFunction>();
         } else {
             types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
+            AutoThreadSafeAccess ts(typeObj);
+
             JS_ASSERT(typeObj);
             if (!typeObj->interpretedFunction) {
                 targets.clear();
                 return true;
             }
 
             fun = typeObj->interpretedFunction;
             *gotLambda = true;
@@ -390,19 +392,16 @@ IonBuilder::canInlineTarget(JSFunction *
     }
 
     if (target->isHeavyweight())
         return DontInline(inlineScript, "Heavyweight function");
 
     if (inlineScript->uninlineable())
         return DontInline(inlineScript, "Uninlineable script");
 
-    if (!inlineScript->analyzedArgsUsage())
-        return DontInline(inlineScript, "Script without analyzed args usage");
-
     if (inlineScript->needsArgsObj())
         return DontInline(inlineScript, "Script that needs an arguments object");
 
     if (!inlineScript->compileAndGo())
         return DontInline(inlineScript, "Non-compileAndGo script");
 
     types::TypeObjectKey *targetType = types::TypeObjectKey::get(target);
     if (targetType->unknownProperties())
@@ -3883,27 +3882,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);
+        AutoThreadSafeAccess ts(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(),
@@ -3925,16 +3928,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) {
+            AutoThreadSafeAccess ts(calleeScript);
             calleeScript->setUninlineable();
             abortReason_ = AbortReason_Inlining;
         } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) {
             abortReason_ = AbortReason_Inlining;
         }
 
         return false;
     }
@@ -3954,16 +3958,17 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     if (returns.empty()) {
         // Inlining of functions that have no exit is not supported.
+        AutoThreadSafeAccess ts(calleeScript);
         calleeScript->setUninlineable();
         abortReason_ = AbortReason_Inlining;
         return false;
     }
     MDefinition *retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock);
     if (!retvalDefn)
         return false;
     returnBlock->push(retvalDefn);
@@ -4621,16 +4626,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();
+    AutoThreadSafeAccess ts(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.
@@ -4648,16 +4654,17 @@ 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();
+    AutoThreadSafeAccess ts(templateObj);
 
     // If the CallObject needs dynamic slots, allocate those now.
     MInstruction *slots;
     if (templateObj->hasDynamicSlots()) {
         size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlotsForCompilation(),
                                                     templateObj->lastProperty()->slotSpan(templateObj->getClass()));
         slots = MNewSlots::New(alloc(), nslots);
     } else {
@@ -4749,20 +4756,23 @@ IonBuilder::createThisScriptedSingleton(
         return nullptr;
 
     JSObject *templateObject = inspector->getTemplateObject(pc);
     if (!templateObject || !templateObject->is<JSObject>())
         return nullptr;
     if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto)
         return nullptr;
 
-    if (!target->nonLazyScript()->types)
-        return nullptr;
-    if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(templateObject)))
-        return nullptr;
+    {
+        AutoThreadSafeAccess ts(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;
@@ -5181,16 +5191,19 @@ 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();
+
+    AutoThreadSafeAccess ts(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());
     for (size_t i = 0; i < expected_args; i++) {
         if (!ArgumentTypesMatch(callInfo.getArg(i), types::TypeScript::ArgTypes(targetScript, i)))
@@ -5396,16 +5409,17 @@ 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())
         {
             JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom();
+            AutoThreadSafeAccess ts(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);
@@ -5471,18 +5485,20 @@ IonBuilder::jsop_newarray(uint32_t count
     MNewArray *ins = MNewArray::New(alloc(), constraints(), count, templateObject,
                                     templateObject->type()->initialHeap(constraints()),
                                     MNewArray::NewArray_Allocating);
     current->add(ins);
     current->push(ins);
 
     types::TemporaryTypeSet::DoubleConversion conversion =
         ins->resultTypeSet()->convertDoubleElements(constraints());
-    if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles)
+    if (conversion == types::TemporaryTypeSet::AlwaysConvertToDoubles) {
+        AutoThreadSafeAccess ts(templateObject);
         templateObject->setShouldConvertDoubleElements();
+    }
     return true;
 }
 
 bool
 IonBuilder::jsop_newobject()
 {
     // Don't bake in the TypeObject for non-CNG scripts.
     JS_ASSERT(script()->compileAndGo());
@@ -5550,17 +5566,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();
+    AutoThreadSafeAccess ts(templateObject);
+
+    if (templateObject->shouldConvertDoubleElements()) {
         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);
@@ -5579,16 +5598,17 @@ IonBuilder::jsop_initelem_array()
 
 bool
 IonBuilder::jsop_initprop(PropertyName *name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     JSObject *templateObject = obj->toNewObject()->templateObject();
+    AutoThreadSafeAccess ts(templateObject);
 
     Shape *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);
@@ -9116,16 +9136,19 @@ IonBuilder::jsop_regexp(RegExpObject *re
     // 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.
+    AutoThreadSafeAccess ts(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);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -84,16 +84,17 @@ class JitCode : public gc::BarrieredCell
         return jumpRelocTableOffset() + jumpRelocTableBytes_;
     }
     uint32_t preBarrierTableOffset() const {
         return dataRelocTableOffset() + dataRelocTableBytes_;
     }
 
   public:
     uint8_t *raw() const {
+        AutoThreadSafeAccess ts(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
@@ -782,16 +782,20 @@ 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().
 
+    AutoThreadSafeAccess ts0(templateObject);
+    AutoThreadSafeAccess ts1(templateObject->lastProperty());
+    AutoThreadSafeAccess ts2(templateObject->lastProperty()->base()); // For isNative() assertions.
+
     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());
--- 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) {
+        AutoThreadSafeAccess ts(templateObject);
         templateObject->setShouldConvertDoubleElements();
+    }
 
     MNewArray *ins = MNewArray::New(alloc(), constraints(), initLength, templateObject,
                                     templateObject->type()->initialHeap(constraints()),
                                     allocating);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -664,19 +664,20 @@ 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();
-        size_t length = JS_GetStringLength(value.toString());
-
-        return MConstant::New(alloc, Int32Value(length));
+        JSAtom *atom = &value.toString()->asAtom();
+
+        AutoThreadSafeAccess ts(atom);
+        return MConstant::New(alloc, Int32Value(atom->length()));
     }
 
     return this;
 }
 
 void
 MFloor::trySpecializeFloat32(TempAllocator &alloc)
 {
@@ -2507,16 +2508,17 @@ MBeta::printOpcode(FILE *fp) const
     sp.init();
     comparison_->print(sp);
     fprintf(fp, " %s", sp.string());
 }
 
 bool
 MNewObject::shouldUseVM() const
 {
+    AutoThreadSafeAccess ts(templateObject());
     return templateObject()->hasSingletonType() ||
            templateObject()->hasDynamicSlots();
 }
 
 bool
 MNewArray::shouldUseVM() const
 {
     JS_ASSERT(count() < JSObject::NELEMENTS_LIMIT);
--- 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) {
+            AutoThreadSafeAccess ts(script);
             uint32_t stackDepth;
             bool reachablePC;
             if (!ReconstructStackDepth(GetIonContext()->cx, script, bailPC, &stackDepth, &reachablePC))
                 return false;
 
             if (reachablePC) {
                 if (JSOp(*bailPC) == JSOP_FUNCALL) {
                     // For fun.call(this, ...); the reconstructStackDepth will
--- 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)
 {
+    AutoThreadSafeAccess ts(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/jsboolinlines.h
+++ b/js/src/jsboolinlines.h
@@ -24,15 +24,17 @@ BooleanGetPrimitiveValue(HandleObject ob
         return obj->as<BooleanObject>().unbox();
 
     return BooleanGetPrimitiveValueSlow(obj, cx);
 }
 
 inline bool
 EmulatesUndefined(JSObject *obj)
 {
+    AutoThreadSafeAccess ts0(obj);
+    AutoThreadSafeAccess ts1(obj->typeRaw());
     JSObject *actual = MOZ_LIKELY(!obj->is<WrapperObject>()) ? obj : UncheckedUnwrap(obj);
     return actual->getClass()->emulatesUndefined();
 }
 
 } /* namespace js */
 
 #endif /* jsboolinlines_h */
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -17,17 +17,24 @@ 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);
+#ifdef DEBUG
+    if (global_) {
+        js::AutoThreadSafeAccess ts0(global_);
+        js::AutoThreadSafeAccess ts1(global_->lastProperty());
+        js::AutoThreadSafeAccess ts2(global_->lastProperty()->base());
+        JS_ASSERT(global_->compartment() == this);
+    }
+#endif
     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
@@ -93,17 +93,17 @@ 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() ||
+        return nonLazyScript()->hasAnyAliasedBindings() ||
                nonLazyScript()->funHasExtensibleScope() ||
                nonLazyScript()->funNeedsDeclEnvObject() ||
                isGenerator();
     }
 
     size_t nargs() const {
         js::AutoThreadSafeAccess ts(this);
         return nargs_;
@@ -171,17 +171,17 @@ class JSFunction : public JSObject
     }
     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. */
@@ -231,17 +231,21 @@ class JSFunction : public JSObject
 
     void setArrow() {
         flags_ |= ARROW;
     }
 
     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::AutoThreadSafeAccess ts(this);
+        return atom_;
+    }
 
     void setGuessedAtom(JSAtom *atom) {
         JS_ASSERT(atom_ == nullptr);
         JS_ASSERT(atom != nullptr);
         JS_ASSERT(!hasGuessedAtom());
         atom_ = atom;
         flags_ |= HAS_GUESSED_ATOM;
     }
@@ -249,16 +253,17 @@ class JSFunction : public JSObject
     /* uint16_t representation bounds number of call object dynamic slots. */
     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::AutoThreadSafeAccess ts(this);
         JS_ASSERT(isInterpreted());
         return u.i.env_;
     }
 
     void setEnvironment(JSObject *obj) {
         JS_ASSERT(isInterpreted());
         *(js::HeapPtrObject *)&u.i.env_ = obj;
     }
@@ -324,33 +329,36 @@ class JSFunction : public JSObject
             flags_ |= INTERPRETED;
             initScript(script);
         }
         JS_ASSERT(hasScript());
         return u.i.s.script_;
     }
 
     JSScript *nonLazyScript() const {
+        js::AutoThreadSafeAccess ts(this);
         JS_ASSERT(hasScript());
         JS_ASSERT(js::CurrentThreadCanReadCompilationData());
         return u.i.s.script_;
     }
 
     js::HeapPtrScript &mutableScript() {
         JS_ASSERT(isInterpreted());
         return *(js::HeapPtrScript *)&u.i.s.script_;
     }
 
     js::LazyScript *lazyScript() const {
+        js::AutoThreadSafeAccess ts(this);
         JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
         JS_ASSERT(js::CurrentThreadCanReadCompilationData());
         return u.i.s.lazy_;
     }
 
     js::LazyScript *lazyScriptOrNull() const {
+        js::AutoThreadSafeAccess ts(this);
         JS_ASSERT(isInterpretedLazy());
         JS_ASSERT(js::CurrentThreadCanReadCompilationData());
         return u.i.s.lazy_;
     }
 
     js::GeneratorKind generatorKind() const {
         if (!isInterpreted())
             return js::NotGenerator;
@@ -391,16 +399,17 @@ class JSFunction : public JSObject
     void initLazyScript(js::LazyScript *lazy) {
         JS_ASSERT(isInterpreted());
         flags_ &= ~INTERPRETED;
         flags_ |= INTERPRETED_LAZY;
         u.i.s.lazy_ = lazy;
     }
 
     JSNative native() const {
+        js::AutoThreadSafeAccess ts(this);
         JS_ASSERT(isNative());
         return u.n.native;
     }
 
     JSNative maybeNative() const {
         return isInterpreted() ? nullptr : native();
     }
 
@@ -415,16 +424,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::AutoThreadSafeAccess ts(this);
         JS_ASSERT(isNative());
         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
@@ -681,16 +681,17 @@ types::NewCompilerConstraintList(jit::Te
 
 /* static */ bool
 TypeScript::FreezeTypeSets(CompilerConstraintList *constraints, JSScript *script,
                            TemporaryTypeSet **pThisTypes,
                            TemporaryTypeSet **pArgTypes,
                            TemporaryTypeSet **pBytecodeTypes)
 {
     JS_ASSERT(CurrentThreadCanReadCompilationData());
+    AutoThreadSafeAccess ts(script);
 
     LifoAlloc *alloc = constraints->alloc();
     StackTypeSet *existing = script->types->typeArray();
 
     size_t count = NumTypeSets(script);
     TemporaryTypeSet *types = alloc->newArrayUninitialized<TemporaryTypeSet>(count);
     if (!types)
         return false;
@@ -801,29 +802,30 @@ TypeObjectKey::proto()
 {
     JS_ASSERT(hasTenuredProto());
     return isTypeObject() ? asTypeObject()->proto() : asSingleObject()->getTaggedProto();
 }
 
 bool
 ObjectImpl::hasTenuredProto() const
 {
+    AutoThreadSafeAccess ts(this);
     return type_->hasTenuredProto();
 }
 
 bool
 TypeObjectKey::hasTenuredProto()
 {
     return isTypeObject() ? asTypeObject()->hasTenuredProto() : asSingleObject()->hasTenuredProto();
 }
 
 JSObject *
 TypeObjectKey::singleton()
 {
-    return isTypeObject() ? asTypeObject()->singleton : asSingleObject();
+    return isTypeObject() ? asTypeObject()->singleton() : asSingleObject();
 }
 
 TypeNewScript *
 TypeObjectKey::newScript()
 {
     if (isTypeObject() && asTypeObject()->hasNewScript())
         return asTypeObject()->newScript();
     return nullptr;
@@ -1381,17 +1383,17 @@ class ConstraintDataFreezeObjectForTyped
       : viewData(viewData)
     {}
 
     const char *kind() { return "freezeObjectForTypedArrayBuffer"; }
 
     bool invalidateOnNewType(Type type) { return false; }
     bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
     bool invalidateOnNewObjectState(TypeObject *object) {
-        return object->singleton->as<TypedArrayObject>().viewData() != viewData;
+        return object->singleton()->as<TypedArrayObject>().viewData() != viewData;
     }
 
     bool constraintHolds(JSContext *cx,
                          const HeapTypeSetKey &property, TemporaryTypeSet *expected)
     {
         return !invalidateOnNewObjectState(property.object()->maybeType());
     }
 
@@ -2201,17 +2203,17 @@ TypeZone::addPendingRecompile(JSContext 
         ObjectStateChange(cx, script->function()->type(), false);
 }
 
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment()->types);
     JS_ASSERT(!(target->flags() & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
-    JS_ASSERT(!target->singleton);
+    JS_ASSERT(!target->singleton());
     JS_ASSERT(target->unknownProperties());
 
     AutoEnterAnalysis enter(cx);
 
     /*
      * Mark both persistent and transient type sets which contain obj as having
      * a generic object type. It is not sufficient to mark just the persistent
      * sets, as analysis of individual opcodes can pull type objects from
@@ -2664,37 +2666,37 @@ TypeCompartment::newTypedObject(JSContex
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 #ifdef DEBUG
 void
-TypeObject::assertCanAccessProto()
+TypeObject::assertCanAccessProto() const
 {
     // The proto pointer for type objects representing singletons may move.
-    JS_ASSERT_IF(singleton, CurrentThreadCanReadCompilationData());
+    JS_ASSERT_IF(singleton(), CurrentThreadCanReadCompilationData());
 
     // Any proto pointer which is in the nursery may be moved, and may not be
     // accessed during off thread compilation.
 #if defined(JSGC_GENERATIONAL) && defined(JS_WORKER_THREADS)
     PerThreadData *pt = TlsPerThreadData.get();
     TaggedProto proto(proto_);
     JS_ASSERT_IF(proto.isObject() && !proto.toObject()->isTenured(),
                  !pt || !pt->ionCompiling);
 #endif
 }
 #endif // DEBUG
 
 void
 TypeObject::setProto(JSContext *cx, TaggedProto proto)
 {
     JS_ASSERT(CurrentThreadCanWriteCompilationData());
-    JS_ASSERT(singleton);
+    JS_ASSERT(singleton());
 
     if (proto.isObject() && IsInsideNursery(cx->runtime(), proto.toObject()))
         addFlags(OBJECT_FLAG_NURSERY_PROTO);
 
     setProtoUnchecked(proto);
 }
 
 static inline void
@@ -2732,51 +2734,50 @@ TypeObject::addProperty(ExclusiveContext
 {
     JS_ASSERT(!*pprop);
     Property *base = cx->typeLifoAlloc().new_<Property>(id);
     if (!base) {
         cx->compartment()->types.setPendingNukeTypes(cx);
         return false;
     }
 
-    if (singleton && singleton->isNative()) {
+    if (singleton() && singleton()->isNative()) {
         /*
          * Fill the property in with any type the object already has in an own
          * property. We are only interested in plain native properties and
          * dense elements which don't go through a barrier when read by the VM
          * or jitcode.
          */
 
-        RootedObject rSingleton(cx, singleton);
         if (JSID_IS_VOID(id)) {
             /* Go through all shapes on the object to get integer-valued properties. */
-            RootedShape shape(cx, singleton->lastProperty());
+            RootedShape shape(cx, singleton()->lastProperty());
             while (!shape->isEmptyShape()) {
                 if (JSID_IS_VOID(IdToTypeId(shape->propid())))
-                    UpdatePropertyType(cx, &base->types, rSingleton, shape, true);
+                    UpdatePropertyType(cx, &base->types, singleton(), shape, true);
                 shape = shape->previous();
             }
 
             /* Also get values of any dense elements in the object. */
-            for (size_t i = 0; i < singleton->getDenseInitializedLength(); i++) {
-                const Value &value = singleton->getDenseElement(i);
+            for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) {
+                const Value &value = singleton()->getDenseElement(i);
                 if (!value.isMagic(JS_ELEMENTS_HOLE)) {
                     Type type = GetValueType(value);
                     if (!base->types.TypeSet::addType(type, &cx->typeLifoAlloc()))
                         cx->compartment()->types.setPendingNukeTypes(cx);
                 }
             }
         } else if (!JSID_IS_EMPTY(id)) {
             RootedId rootedId(cx, id);
-            Shape *shape = singleton->nativeLookup(cx, rootedId);
+            Shape *shape = singleton()->nativeLookup(cx, rootedId);
             if (shape)
-                UpdatePropertyType(cx, &base->types, rSingleton, shape, false);
+                UpdatePropertyType(cx, &base->types, singleton(), shape, false);
         }
 
-        if (singleton->watched()) {
+        if (singleton()->watched()) {
             /*
              * Mark the property as configured, to inhibit optimizations on it
              * and avoid bypassing the watchpoint handler.
              */
             base->types.setConfiguredProperty(cx);
         }
     }
 
@@ -2936,20 +2937,20 @@ TypeObject::markStateChange(ExclusiveCon
 void
 TypeObject::setFlags(ExclusiveContext *cx, TypeObjectFlags flags)
 {
     if (hasAllFlags(flags))
         return;
 
     AutoEnterAnalysis enter(cx);
 
-    if (singleton) {
+    if (singleton()) {
         /* Make sure flags are consistent with persistent object state. */
         JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
-                     singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
+                     singleton()->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
     }
 
     {
         AutoLockForCompilation lock(cx);
         addFlags(flags);
     }
 
     InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
@@ -3756,17 +3757,17 @@ JSObject::makeLazyType(JSContext *cx, Ha
         obj->type_ = type;
         return type;
     }
 
     AutoEnterAnalysis enter(cx);
 
     /* Fill in the type according to the state of this object. */
 
-    type->singleton = obj;
+    type->initSingleton(obj);
 
     if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
         type->interpretedFunction = &obj->as<JSFunction>();
 
     {
         AutoLockForCompilation lock(cx);
         obj->type_ = type;
     }
@@ -3982,17 +3983,17 @@ ExclusiveContext::getLazyType(const Clas
     Rooted<TaggedProto> protoRoot(this, proto);
     TypeObject *type = compartment()->types.newTypeObject(this, clasp, protoRoot);
     if (!type)
         return nullptr;
 
     if (!table.add(p, TypeObjectWithNewScriptEntry(type, nullptr)))
         return nullptr;
 
-    type->singleton = (JSObject *) TypeObject::LAZY_SINGLETON;
+    type->initSingleton((JSObject *) TypeObject::LAZY_SINGLETON);
 
     return type;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
@@ -4082,17 +4083,17 @@ TypeObject::sweep(FreeOp *fop)
         unsigned oldCapacity = HashSetCapacity(propertyCount);
         Property **oldArray = propertySet;
 
         clearProperties();
         propertyCount = 0;
         for (unsigned i = 0; i < oldCapacity; i++) {
             Property *prop = oldArray[i];
             if (prop) {
-                if (singleton && !prop->types.constraintList) {
+                if (singleton() && !prop->types.constraintList) {
                     /*
                      * Don't copy over properties of singleton objects which
                      * don't have associated constraints. The contents of these
                      * type sets will be regenerated as necessary.
                      */
                     continue;
                 }
                 Property *newProp = typeLifoAlloc.new_<Property>(*prop);
@@ -4109,17 +4110,17 @@ TypeObject::sweep(FreeOp *fop)
                 } else {
                     zone()->types.setPendingNukeTypes();
                 }
             }
         }
         setBasePropertyCount(propertyCount);
     } else if (propertyCount == 1) {
         Property *prop = (Property *) propertySet;
-        if (singleton && !prop->types.constraintList) {
+        if (singleton() && !prop->types.constraintList) {
             // Skip, as above.
             clearProperties();
         } else {
             Property *newProp = typeLifoAlloc.new_<Property>(*prop);
             if (newProp) {
                 propertySet = (Property **) newProp;
                 newProp->types.sweep(zone());
             } else {
@@ -4301,26 +4302,16 @@ TypeCompartment::addSizeOfExcludingThis(
             *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
         }
     }
 }
 
 size_t
 TypeObject::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    if (singleton) {
-        /*
-         * Properties and associated type sets for singletons are cleared on
-         * every GC. The type object is normally destroyed too, but we don't
-         * charge this to 'temporary' as this is not for GC heap values.
-         */
-        JS_ASSERT(!hasNewScript());
-        return 0;
-    }
-
     return mallocSizeOf(addendum);
 }
 
 TypeZone::TypeZone(Zone *zone)
   : zone_(zone),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     compilerOutputs(nullptr),
     pendingRecompiles(nullptr),
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -884,61 +884,71 @@ struct TypeObject : gc::BarrieredCell<Ty
   private:
     /* Class shared by object using this type. */
     const Class *clasp_;
 
     /* Prototype shared by objects using this type. */
     HeapPtrObject proto_;
 
 #ifdef DEBUG
-    void assertCanAccessProto();
+    void assertCanAccessProto() const;
 #else
-    void assertCanAccessProto() {}
+    void assertCanAccessProto() const {}
 #endif
 
+    /*
+     * 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_;
+
   public:
 
-    const Class *clasp() {
+    const Class *clasp() const {
+        AutoThreadSafeAccess ts(this);
         return clasp_;
     }
 
     void setClasp(const Class *clasp) {
         JS_ASSERT(CurrentThreadCanWriteCompilationData());
-        JS_ASSERT(singleton);
+        JS_ASSERT(singleton());
         clasp_ = clasp;
     }
 
-    TaggedProto proto() {
+    TaggedProto proto() const {
+        AutoThreadSafeAccess ts(this);
         assertCanAccessProto();
         return TaggedProto(proto_);
     }
 
-    HeapPtrObject &protoRaw() {
-        // For use during marking, don't call otherwise.
-        return proto_;
+    JSObject *singleton() const {
+        AutoThreadSafeAccess ts(this);
+        return singleton_;
     }
 
+    // For use during marking, don't call otherwise.
+    HeapPtrObject &protoRaw() { return proto_; }
+    HeapPtrObject &singletonRaw() { return singleton_; }
+
     void setProto(JSContext *cx, TaggedProto proto);
     void setProtoUnchecked(TaggedProto proto) {
         proto_ = proto.raw();
     }
 
-    /*
-     * 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;
+    void initSingleton(JSObject *singleton) {
+        singleton_ = 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 { return singleton() == (JSObject *) LAZY_SINGLETON; }
 
   private:
     /* Flags for this object. */
     TypeObjectFlags flags_;
 
     /*
      * This field allows various special classes of objects to attach
      * additional information to a type object:
@@ -949,46 +959,51 @@ struct TypeObject : gc::BarrieredCell<Ty
      *   some number of properties to the object in a definite order
      *   before the object escapes.
      */
     HeapPtr<TypeObjectAddendum> addendum;
   public:
 
     TypeObjectFlags flags() const {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
+        AutoThreadSafeAccess ts(this);
         return flags_;
     }
 
     void addFlags(TypeObjectFlags flags) {
         JS_ASSERT(CurrentThreadCanWriteCompilationData());
         flags_ |= flags;
     }
 
     void clearFlags(TypeObjectFlags flags) {
         JS_ASSERT(CurrentThreadCanWriteCompilationData());
         flags_ &= ~flags;
     }
 
     bool hasNewScript() const {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
+        AutoThreadSafeAccess ts(this);
         return addendum && addendum->isNewScript();
     }
 
     TypeNewScript *newScript() {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
+        AutoThreadSafeAccess ts(this);
         return addendum->asNewScript();
     }
 
     bool hasTypedObject() {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
+        AutoThreadSafeAccess ts(this);
         return addendum && addendum->isTypedObject();
     }
 
     TypeTypedObject *typedObject() {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
+        AutoThreadSafeAccess ts(this);
         return addendum->asTypedObject();
     }
 
     void setAddendum(TypeObjectAddendum *addendum) {
         JS_ASSERT(CurrentThreadCanWriteCompilationData());
         this->addendum = addendum;
     }
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -83,18 +83,19 @@ 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);
+    AutoThreadSafeAccess ts(obj);
+    if (obj->singleton())
+        return Type(uintptr_t(obj->singleton()) | 1);
     return Type(uintptr_t(obj));
 }
 
 /* static */ inline Type
 Type::ObjectType(TypeObjectKey *obj)
 {
     return Type(uintptr_t(obj));
 }
@@ -172,18 +173,19 @@ 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);
-        JS::TwoByteChars cp = str->range();
+        JSAtom *atom = JSID_TO_ATOM(id);
+        js::AutoThreadSafeAccess ts(atom);
+        JS::TwoByteChars cp = atom->range();
         if (cp.length() > 0 && (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;
         }
         return id;
@@ -1080,17 +1082,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->singleton());
         if (nobject->unknownProperties())
             goto unknownObject;
     }
 
     if (false) {
     unknownObject:
         type = Type::AnyObjectType();
         flags |= TYPE_FLAG_ANYOBJECT;
@@ -1312,16 +1314,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());
     JS_ASSERT(CurrentThreadCanReadCompilationData());
 
+    AutoThreadSafeAccess ts(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
@@ -5501,16 +5501,18 @@ DumpProperty(JSObject *obj, Shape &shape
         fprintf(stderr, " (INVALID!)");
     }
     fprintf(stderr, "\n");
 }
 
 bool
 JSObject::uninlinedIsProxy() const
 {
+    AutoThreadSafeAccess ts0(this);
+    AutoThreadSafeAccess ts1(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::AutoThreadSafeAccess ts0(this);
+        js::AutoThreadSafeAccess ts1(lastProperty());
+        js::AutoThreadSafeAccess ts2(lastProperty()->base());
         return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
     }
 
     inline bool hasSpecialEquality() const;
 
     bool watched() const {
         return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3109,22 +3109,24 @@ JSScript::argumentsOptimizationFailed(JS
     }
 
     return true;
 }
 
 bool
 JSScript::varIsAliased(unsigned varSlot)
 {
+    AutoThreadSafeAccess ts(this);
     return bindings.bindingIsAliased(bindings.numArgs() + varSlot);
 }
 
 bool
 JSScript::formalIsAliased(unsigned argSlot)
 {
+    AutoThreadSafeAccess ts(this);
     return bindings.bindingIsAliased(argSlot);
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -198,21 +198,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.
      */
@@ -238,17 +240,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.
+        AutoThreadSafeAccess ts(callObjShape_);
+        return !callObjShape_->isEmptyShape();
+    }
 
     void trace(JSTracer *trc);
 };
 
 template <>
 struct GCMethods<Bindings> {
     static Bindings initial();
     static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
@@ -473,16 +482,20 @@ class ScriptSourceObject : public JSObje
   public:
     static const Class class_;
 
     static void finalize(FreeOp *fop, JSObject *obj);
     static ScriptSourceObject *create(ExclusiveContext *cx, ScriptSource *source,
                                       const ReadOnlyCompileOptions &options);
 
     ScriptSource *source() {
+        // Script source objects are immutable.
+        AutoThreadSafeAccess ts0(this);
+        AutoThreadSafeAccess ts1(lastProperty());
+        AutoThreadSafeAccess ts2(lastProperty()->base());
         return static_cast<ScriptSource *>(getReservedSlot(SOURCE_SLOT).toPrivate());
     }
 
     void setSource(ScriptSource *source);
 
     JSObject *element() const;
     const Value &elementProperty() const;
 
@@ -551,16 +564,36 @@ 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 hasAnyAliasedBindings() const {
+        js::AutoThreadSafeAccess ts(this);
+        return bindings.hasAnyAliasedBindings();
+    }
+
+    js::Binding *bindingArray() const {
+        js::AutoThreadSafeAccess ts(this);
+        return bindings.bindingArray();
+    }
+
+    unsigned numArgs() const {
+        js::AutoThreadSafeAccess ts(this);
+        return bindings.numArgs();
+    }
+
+    js::Shape *callObjShape() const {
+        js::AutoThreadSafeAccess ts(this);
+        return bindings.callObjShape();
+    }
+
     // Word-sized fields.
 
   private:
     jsbytecode      *code_;     /* bytecodes and their immediate operands */
   public:
     uint8_t         *data;      /* pointer to variable-length data array (see
                                    comment above Create() for details) */
 
@@ -884,22 +917,34 @@ class JSScript : public js::gc::Barriere
 
     bool compileAndGo() const {
         js::AutoThreadSafeAccess ts(this);
         return compileAndGo_;
     }
 
     bool selfHosted() const { return selfHosted_; }
     bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; }
-    bool funHasExtensibleScope() const { return funHasExtensibleScope_; }
-    bool funNeedsDeclEnvObject() const { return funNeedsDeclEnvObject_; }
-    bool funHasAnyAliasedFormal() const { return funHasAnyAliasedFormal_; }
+    bool funHasExtensibleScope() const {
+        js::AutoThreadSafeAccess ts(this);
+        return funHasExtensibleScope_;
+    }
+    bool funNeedsDeclEnvObject() const {
+        js::AutoThreadSafeAccess ts(this);
+        return funNeedsDeclEnvObject_;
+    }
+    bool funHasAnyAliasedFormal() const {
+        js::AutoThreadSafeAccess ts(this);
+        return funHasAnyAliasedFormal_;
+    }
 
     bool hasSingletons() const { return hasSingletons_; }
-    bool treatAsRunOnce() const { return treatAsRunOnce_; }
+    bool treatAsRunOnce() const {
+        js::AutoThreadSafeAccess ts(this);
+        return treatAsRunOnce_;
+    }
     bool hasRunOnce() const { return hasRunOnce_; }
     bool hasBeenCloned() const { return hasBeenCloned_; }
 
     void setTreatAsRunOnce() { treatAsRunOnce_ = true; }
     void setHasRunOnce() { hasRunOnce_ = true; }
     void setHasBeenCloned() { hasBeenCloned_ = true; }
 
     bool isActiveEval() const { return isActiveEval_; }
@@ -916,33 +961,60 @@ class JSScript : public js::gc::Barriere
         JS_ASSERT(isCachedEval() && !isActiveEval());
         isCachedEval_ = false;
         isActiveEval_ = true;
     }
 
     void setActiveEval() { isActiveEval_ = true; }
     void setDirectlyInsideEval() { directlyInsideEval_ = true; }
 
-    bool usesArgumentsAndApply() const { return usesArgumentsAndApply_; }
+    bool usesArgumentsAndApply() const {
+        js::AutoThreadSafeAccess ts(this);
+        return usesArgumentsAndApply_;
+    }
     void setUsesArgumentsAndApply() { usesArgumentsAndApply_ = true; }
 
-    bool shouldCloneAtCallsite() const { return shouldCloneAtCallsite_; }
-    bool shouldInline() const { return shouldInline_; }
+    bool shouldCloneAtCallsite() const {
+        js::AutoThreadSafeAccess ts(this);
+        return shouldCloneAtCallsite_;
+    }
+    bool shouldInline() const {
+        js::AutoThreadSafeAccess ts(this);
+        return shouldInline_;
+    }
 
     void setShouldCloneAtCallsite() { shouldCloneAtCallsite_ = true; }
     void setShouldInline() { shouldInline_ = true; }
 
-    bool isCallsiteClone() const { return isCallsiteClone_; }
+    bool isCallsiteClone() const {
+        js::AutoThreadSafeAccess ts(this);
+        return isCallsiteClone_;
+    }
     bool isGeneratorExp() const { return isGeneratorExp_; }
 
-    bool failedBoundsCheck() const { return failedBoundsCheck_; }
-    bool failedShapeGuard() const { return failedShapeGuard_; }
-    bool hadFrequentBailouts() const { return hadFrequentBailouts_; }
-    bool uninlineable() const { return uninlineable_; }
-    bool invalidatedIdempotentCache() const { return invalidatedIdempotentCache_; }
+    bool failedBoundsCheck() const {
+        js::AutoThreadSafeAccess ts(this);
+        return failedBoundsCheck_;
+    }
+    bool failedShapeGuard() const {
+        js::AutoThreadSafeAccess ts(this);
+        return failedShapeGuard_;
+    }
+    bool hadFrequentBailouts() const {
+        js::AutoThreadSafeAccess ts(this);
+        return hadFrequentBailouts_;
+    }
+    bool uninlineable() const {
+        js::AutoThreadSafeAccess ts(this);
+        return uninlineable_;
+    }
+    bool invalidatedIdempotentCache() const {
+        js::AutoThreadSafeAccess ts(this);
+        return invalidatedIdempotentCache_;
+    }
 
     void setFailedBoundsCheck() { failedBoundsCheck_ = true; }
     void setFailedShapeGuard() { failedShapeGuard_ = true; }
     void setHadFrequentBailouts() { hadFrequentBailouts_ = true; }
     void setUninlineable() { uninlineable_ = true; }
     void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; }
 
     bool hasScriptCounts() const { return hasScriptCounts_; }
@@ -950,24 +1022,28 @@ class JSScript : public js::gc::Barriere
     bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
     void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
     void clearHasFreezeConstraints() { hasFreezeConstraints_ = false; }
 
     bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
     void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
 
     /* See ContextFlags::funArgumentsHasLocalBinding comment. */
-    bool argumentsHasVarBinding() const { return argsHasVarBinding_; }
+    bool argumentsHasVarBinding() const {
+        js::AutoThreadSafeAccess ts(this);
+        return argsHasVarBinding_;
+    }
     jsbytecode *argumentsBytecode() const { JS_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); }
     void setArgumentsHasVarBinding();
     bool argumentsAliasesFormals() const {
         return argumentsHasVarBinding() && !strict();
     }
 
     js::GeneratorKind generatorKind() const {
+        js::AutoThreadSafeAccess ts(this);
         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; }
     void setGeneratorKind(js::GeneratorKind kind) {
         // A script only gets its generator kind set as part of initialization,
         // so it can only transition from not being a generator.
@@ -982,18 +1058,19 @@ class JSScript : public js::gc::Barriere
      * 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::AutoThreadSafeAccess ts(this);
+        JS_ASSERT(js::CurrentThreadCanReadCompilationData());
         JS_ASSERT(analyzedArgsUsage());
-        JS_ASSERT(js::CurrentThreadCanReadCompilationData());
         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
@@ -1007,19 +1084,21 @@ class JSScript : public js::gc::Barriere
         return needsArgsObj() && !strict();
     }
 
     bool hasAnyIonScript() const {
         return hasIonScript() || hasParallelIonScript();
     }
 
     bool hasIonScript() const {
+        js::AutoThreadSafeAccess ts(this);
         return ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT;
     }
     bool canIonCompile() const {
+        js::AutoThreadSafeAccess ts(this);
         return ion != ION_DISABLED_SCRIPT;
     }
 
     bool isIonCompilingOffThread() const {
         return ion == ION_COMPILING_SCRIPT;
     }
 
     js::jit::IonScript *ionScript() const {
@@ -1036,34 +1115,37 @@ class JSScript : public js::gc::Barriere
         if (hasIonScript())
             js::jit::IonScript::writeBarrierPre(tenuredZone(), ion);
         ion = ionScript;
         updateBaselineOrIonRaw();
     }
 
     bool hasBaselineScript() const {
         JS_ASSERT(js::CurrentThreadCanReadCompilationData());
+        js::AutoThreadSafeAccess ts(this);
         return baseline && baseline != BASELINE_DISABLED_SCRIPT;
     }
     bool canBaselineCompile() const {
         return baseline != BASELINE_DISABLED_SCRIPT;
     }
     js::jit::BaselineScript *baselineScript() const {
         JS_ASSERT(hasBaselineScript());
+        js::AutoThreadSafeAccess ts(this);
         return baseline;
     }
     inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript);
 
     void updateBaselineOrIonRaw();
 
     bool hasParallelIonScript() const {
         return parallelIon && parallelIon != ION_DISABLED_SCRIPT && parallelIon != ION_COMPILING_SCRIPT;
     }
 
     bool canParallelIonCompile() const {
+        js::AutoThreadSafeAccess ts(this);
         return parallelIon != ION_DISABLED_SCRIPT;
     }
 
     bool isParallelIonCompilingOffThread() const {
         return parallelIon == ION_COMPILING_SCRIPT;
     }
 
     js::jit::IonScript *parallelIonScript() const {
@@ -1094,28 +1176,34 @@ 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::AutoThreadSafeAccess ts(this);
+        return function_;
+    }
     inline void setFunction(JSFunction *fun);
 
     JSFunction *originalFunction() const;
     void setIsCallsiteClone(JSObject *fun);
 
     JSFlatString *sourceData(JSContext *cx);
 
     static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked);
 
     void setSourceObject(JSObject *object);
-    JSObject *sourceObject() const { return sourceObject_; }
+    JSObject *sourceObject() const {
+        js::AutoThreadSafeAccess ts(this);
+        return sourceObject_;
+    }
     js::ScriptSource *scriptSource() const;
     JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); }
     const char *filename() const { return scriptSource()->filename(); }
 
   public:
 
     /* Return whether this script was compiled for 'eval' */
     bool isForEval() { return isCachedEval() || isActiveEval(); }
@@ -1144,16 +1232,17 @@ class JSScript : public js::gc::Barriere
 
     inline js::GlobalObject &global() const;
     js::GlobalObject &uninlinedGlobal() const;
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
         if (isCallsiteClone())
             return nullptr;
+        js::AutoThreadSafeAccess ts(this);
         return enclosingScopeOrOriginalFunction_;
     }
 
     /*
      * If a compile error occurs in an enclosing function after parsing a
      * nested function, the enclosing function's JSFunction, which appears on
      * the nested function's enclosingScope chain, will be invalid. Normal VM
      * operation only sees scripts where all enclosing scripts have been
@@ -1164,17 +1253,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::AutoThreadSafeAccess ts(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);
@@ -1196,18 +1289,21 @@ class JSScript : public js::gc::Barriere
     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()); }
 
-    bool hasArray(ArrayKind kind)           { return (hasArrayBits & (1 << kind)); }
-    void setHasArray(ArrayKind kind)        { hasArrayBits |= (1 << kind); }
+    bool hasArray(ArrayKind kind) {
+        js::AutoThreadSafeAccess ts(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); }
 
@@ -1218,44 +1314,50 @@ class JSScript : public js::gc::Barriere
     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);    }
 
     size_t dataSize() const { return dataSize_; }
 
     js::ConstArray *consts() {
         JS_ASSERT(hasConsts());
+        js::AutoThreadSafeAccess ts(this);
         return reinterpret_cast<js::ConstArray *>(data + constsOffset());
     }
 
     js::ObjectArray *objects() {
         JS_ASSERT(hasObjects());
+        js::AutoThreadSafeAccess ts(this);
         return reinterpret_cast<js::ObjectArray *>(data + objectsOffset());
     }
 
     js::ObjectArray *regexps() {
         JS_ASSERT(hasRegexps());
+        js::AutoThreadSafeAccess ts(this);
         return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset());
     }
 
     js::TryNoteArray *trynotes() {
         JS_ASSERT(hasTrynotes());
+        js::AutoThreadSafeAccess ts(this);
         return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
     }
 
     js::BlockScopeArray *blockScopes() {
         JS_ASSERT(hasBlockScopes());
+        js::AutoThreadSafeAccess ts(this);
         return reinterpret_cast<js::BlockScopeArray *>(data + blockScopesOffset());
     }
 
     bool hasLoops();
 
     size_t natoms() const { return natoms_; }
 
     js::HeapPtrAtom &getAtom(size_t index) const {
+        js::AutoThreadSafeAccess ts(this);
         JS_ASSERT(index < natoms());
         return atoms[index];
     }
 
     js::HeapPtrAtom &getAtom(jsbytecode *pc) const {
         JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
         return getAtom(GET_UINT32_INDEX(pc));
     }
@@ -1599,16 +1701,17 @@ class LazyScript : public gc::BarrieredC
     bool directlyInsideEval() const {
         return directlyInsideEval_;
     }
     void setDirectlyInsideEval() {
         directlyInsideEval_ = true;
     }
 
     bool usesArgumentsAndApply() const {
+        AutoThreadSafeAccess ts(this);
         return usesArgumentsAndApply_;
     }
     void setUsesArgumentsAndApply() {
         usesArgumentsAndApply_ = true;
     }
 
     bool hasBeenCloned() const {
         return hasBeenCloned_;
@@ -1623,19 +1726,21 @@ class LazyScript : public gc::BarrieredC
     void setTreatAsRunOnce() {
         treatAsRunOnce_ = true;
     }
 
     ScriptSource *source() const {
         return sourceObject()->source();
     }
     uint32_t begin() const {
+        AutoThreadSafeAccess ts(this);
         return begin_;
     }
     uint32_t end() const {
+        AutoThreadSafeAccess ts(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->funHasAnyAliasedFormal() ? 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::AutoThreadSafeAccess ts(this);
     return *compartment()->maybeGlobal();
 }
 
 inline JSPrincipals *
 JSScript::principals()
 {
     return compartment()->principals;
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -4137,16 +4137,18 @@ 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)
 {
+    AutoThreadSafeAccess ts0(atom1);
+    AutoThreadSafeAccess ts1(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
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -72,18 +72,25 @@ Wrapper::wrappedObject(JSObject *wrapper
     JS_ASSERT(wrapper->is<WrapperObject>());
     return wrapper->as<ProxyObject>().target();
 }
 
 JS_FRIEND_API(JSObject *)
 js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
 {
     unsigned flags = 0;
-    while (wrapped->is<WrapperObject>() &&
-           !JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) {
+    while (true) {
+        AutoThreadSafeAccess ts0(wrapped);
+        AutoThreadSafeAccess ts1(wrapped->typeRaw());
+        AutoThreadSafeAccess ts2(wrapped->lastProperty());
+        if (!wrapped->is<WrapperObject>() ||
+            JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
+        {
+            break;
+        }
         flags |= Wrapper::wrapperHandler(wrapped)->flags();
         wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
     }
     if (flagsp)
         *flagsp = flags;
     return wrapped;
 }
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -447,25 +447,25 @@ GlobalObject::create(JSContext *cx, cons
 }
 
 /* static */ bool
 GlobalObject::getOrCreateEval(JSContext *cx, Handle<GlobalObject*> global,
                               MutableHandleObject eval)
 {
     if (!global->getOrCreateObjectPrototype(cx))
         return false;
-    eval.set(&global->getSlotRefForCompilation(EVAL).toObject());
+    eval.set(&global->getSlotForCompilation(EVAL).toObject());
     return true;
 }
 
 bool
 GlobalObject::valueIsEval(Value val)
 {
-    HeapSlot &eval = getSlotRef(EVAL);
-    return eval.isObject() && eval.get() == val;
+    Value eval = getSlotForCompilation(EVAL);
+    return eval.isObject() && eval == val;
 }
 
 /* static */ bool
 GlobalObject::initStandardClasses(JSContext *cx, Handle<GlobalObject*> global)
 {
     /* Define a top-level property 'undefined' with the undefined value. */
     RootedValue undefinedValue(cx, UndefinedValue());
     if (!JSObject::defineProperty(cx, global, cx->names().undefined, undefinedValue,
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -150,27 +150,27 @@ class GlobalObject : public JSObject
     void setIntrinsicsHolder(JSObject *obj) {
         JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
         setSlot(INTRINSICS, ObjectValue(*obj));
     }
 
   public:
     Value getConstructor(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlotRefForCompilation(APPLICATION_SLOTS + key);
+        return getSlotForCompilation(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 {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlotRefForCompilation(APPLICATION_SLOTS + JSProto_LIMIT + key);
+        return getSlotForCompilation(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) {
@@ -442,23 +442,24 @@ 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 {
+    Value getSlotForCompilation(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();
+        AutoThreadSafeAccess ts(this);
         if (slot < fixed)
             return fixedSlots()[slot];
         return slots[slot - fixed];
     }
 
   public:
     JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator,
@@ -504,23 +505,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(!getSlotRefForCompilation(INTRINSICS).isUndefined());
-        return &getSlotRefForCompilation(INTRINSICS).toObject();
+        JS_ASSERT(!getSlotForCompilation(INTRINSICS).isUndefined());
+        return &getSlotForCompilation(INTRINSICS).toObject();
     }
 
     bool maybeGetIntrinsicValue(jsid id, Value *vp) {
         JS_ASSERT(CurrentThreadCanReadCompilationData());
         JSObject *holder = intrinsicsHolder();
+
+        AutoThreadSafeAccess ts0(holder);
+        AutoThreadSafeAccess ts1(holder->lastProperty());
+        AutoThreadSafeAccess ts2(holder->lastProperty()->base());
+
         if (Shape *shape = holder->nativeLookupPure(id)) {
             *vp = holder->getSlot(shape->slot());
             return true;
         }
         return false;
     }
     bool maybeGetIntrinsicValue(PropertyName *name, Value *vp) {
         return maybeGetIntrinsicValue(NameToId(name), vp);
@@ -548,17 +554,18 @@ 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 = getSlotRefForCompilation(REGEXP_STATICS).toObject();
+        JSObject &resObj = getSlotForCompilation(REGEXP_STATICS).toObject();
+        AutoThreadSafeAccess ts(&resObj);
         return static_cast<RegExpStatics *>(resObj.getPrivate(/* nfixed = */ 1));
     }
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -975,23 +975,25 @@ class ObjectImpl : public gc::BarrieredC
     JSObject * asObjectPtr() { return reinterpret_cast<JSObject *>(this); }
     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:
-    js::TaggedProto getTaggedProto() const {
+    TaggedProto getTaggedProto() const {
+        AutoThreadSafeAccess ts(this);
         return type_->proto();
     }
 
     bool hasTenuredProto() const;
 
     const Class *getClass() const {
+        AutoThreadSafeAccess ts(this);
         return type_->clasp();
     }
 
     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
@@ -1188,36 +1190,48 @@ class ObjectImpl : public gc::BarrieredC
     }
 
     bool isNative() const {
         return lastProperty()->isNative();
     }
 
     types::TypeObject *type() const {
         MOZ_ASSERT(!hasLazyType());
+        return typeRaw();
+    }
+
+    types::TypeObject *typeRaw() const {
+        AutoThreadSafeAccess ts0(this);
+        AutoThreadSafeAccess ts1(type_);
         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 {
+        AutoThreadSafeAccess ts(this);
+        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 {
+        AutoThreadSafeAccess ts(this);
+        return type_->lazy();
+    }
 
     uint32_t slotSpan() const {
         if (inDictionaryMode())
             return lastProperty()->base()->slotSpan();
         return lastProperty()->slotSpan();
     }
 
     /* Compute dynamicSlotsCount() for this object. */
@@ -1535,16 +1549,20 @@ BarrieredCell<ObjectImpl>::zone() const
     return zone;
 }
 
 template <>
 JS_ALWAYS_INLINE Zone *
 BarrieredCell<ObjectImpl>::zoneFromAnyThread() const
 {
     const ObjectImpl* obj = static_cast<const ObjectImpl*>(this);
+
+    // Note: This read of obj->shape_ may race, though the zone fetched will be the same.
+    AutoThreadSafeAccess ts(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/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()->shouldPreTenure())
+
+    // 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->shouldPreTenure())
         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,17 +10,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include <locale.h>
 #include <string.h>
 
-#if defined(DEBUG) && !defined(XP_WIN)
+#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
 # include <sys/mman.h>
 #endif
 
 #include "jsatom.h"
 #include "jsdtoa.h"
 #include "jsgc.h"
 #include "jsmath.h"
 #include "jsnativestack.h"
@@ -848,17 +848,17 @@ 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)
+#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
 
 AutoProtectHeapForIonCompilation::AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : runtime(rt)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
     JS_ASSERT(!runtime->heapProtected_);
     runtime->heapProtected_ = true;
@@ -897,17 +897,17 @@ AutoThreadSafeAccess::AutoThreadSafeAcce
     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))
+    if (mprotect(arena, sizeof(Arena), PROT_READ | PROT_WRITE))
         MOZ_CRASH();
 
     if (!runtime->unprotectedArenas.append(arena))
         MOZ_CRASH();
 }
 
 AutoThreadSafeAccess::~AutoThreadSafeAccess()
 {
@@ -916,17 +916,17 @@ AutoThreadSafeAccess::~AutoThreadSafeAcc
 
     if (mprotect(arena, sizeof(Arena), PROT_NONE))
         MOZ_CRASH();
 
     JS_ASSERT(arena == runtime->unprotectedArenas.back());
     runtime->unprotectedArenas.popBack();
 }
 
-#endif // DEBUG && !XP_WIN
+#endif // JS_CAN_CHECK_THREADSAFE_ACCESSES
 
 #ifdef JS_WORKER_THREADS
 
 void
 JSRuntime::setUsedByExclusiveThread(Zone *zone)
 {
     JS_ASSERT(!zone->usedByExclusiveThread);
     zone->usedByExclusiveThread = true;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -2135,17 +2135,17 @@ class AutoEnterIonCompilation
 };
 
 // Debugging RAII class which protects the entire GC heap for the duration of an
 // Ion compilation. When used only the main thread will be active and all
 // accesses to GC things must be wrapped by an AutoThreadSafeAccess instance.
 class AutoProtectHeapForIonCompilation
 {
   public:
-#if defined(DEBUG) && !defined(XP_WIN)
+#ifdef JS_CAN_CHECK_THREADSAFE_ACCESSES
     JSRuntime *runtime;
 
     AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoProtectHeapForIonCompilation();
 #else
     AutoProtectHeapForIonCompilation(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -70,19 +70,21 @@ 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) {
+        AutoThreadSafeAccess ts(&block());
+        return block().lastProperty();
+    }
+    return funScript()->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
@@ -94,17 +94,17 @@ js::ScopeCoordinateName(ScopeCoordinateN
     ScopeCoordinate sc(pc);
     if (shape == cache.shape) {
         ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot);
         id = p->value();
     } else {
         Shape::Range<NoGC> r(shape);
         while (r.front().slot() != sc.slot)
             r.popFront();
-        id = r.front().propid();
+        id = r.front().propidRaw();
     }
 
     /* Beware nameless destructuring formal. */
     if (!JSID_IS_ATOM(id))
         return script->runtimeFromAnyThread()->atomState.empty;
     return JSID_TO_ATOM(id)->asPropertyName();
 }
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -176,16 +176,17 @@ 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 {
+        AutoThreadSafeAccess ts(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
@@ -227,27 +228,29 @@ 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 {
+        AutoThreadSafeAccess ts(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 {
+        AutoThreadSafeAccess ts(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());
     }
 
@@ -363,16 +366,17 @@ class BlockObject : public NestedScopeOb
 
 class StaticBlockObject : public BlockObject
 {
   public:
     static StaticBlockObject *create(ExclusiveContext *cx);
 
     /* See StaticScopeIter comment. */
     JSObject *enclosingStaticScope() const {
+        AutoThreadSafeAccess ts(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;
@@ -395,16 +399,17 @@ class StaticBlockObject : public BlockOb
 
     /*
      * A static block object is cloned (when entering the block) iff some
      * variable of the block isAliased.
      */
     bool needsClone() {
         // The first variable slot will always indicate whether the object has
         // any aliased vars. Bypass slotValue() to allow testing this off thread.
+        AutoThreadSafeAccess ts(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());
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -190,17 +190,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
@@ -224,17 +224,17 @@ ShapeTable::search(jsid id, bool adding)
         hash1 &= sizeMask;
         spp = entries + hash1;
 
         stored = *spp;
         if (SHAPE_IS_FREE(stored))
             return (adding && firstRemoved) ? firstRemoved : spp;
 
         shape = SHAPE_CLEAR_COLLISION(stored);
-        if (shape && shape->propid() == id) {
+        if (shape && shape->propidRaw() == id) {
             JS_ASSERT(collision_flag);
             return spp;
         }
 
         if (SHAPE_IS_REMOVED(stored)) {
             if (!firstRemoved)
                 firstRemoved = spp;
         } else {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1029,21 +1029,25 @@ class Shape : public gc::BarrieredCell<S
 
         Shape &front() const {
             JS_ASSERT(!empty());
             return *cursor;
         }
 
         void popFront() {
             JS_ASSERT(!empty());
+            AutoThreadSafeAccess ts(cursor);
             cursor = cursor->parent;
         }
     };
 
-    const Class *getObjectClass() const { return base()->clasp; }
+    const Class *getObjectClass() const {
+        AutoThreadSafeAccess ts(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,
@@ -1090,22 +1094,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; }
-    unsigned getFlags() const  { return flags & PUBLIC_FLAGS; }
+    bool inDictionary() const {
+        AutoThreadSafeAccess ts(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();
     }
@@ -1153,21 +1160,31 @@ 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 {
+        AutoThreadSafeAccess ts(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.
+        AutoThreadSafeAccess ts(this);
+        return slotInfo & SLOT_MASK;
+    }
 
     bool isEmptyShape() const {
+        AutoThreadSafeAccess ts(this);
         JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
         return JSID_IS_EMPTY(propid_);
     }
 
     uint32_t slotSpan(const Class *clasp) const {
         JS_ASSERT(!inDictionary());
         uint32_t free = JSSLOT_FREE(clasp);
         return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
@@ -1179,16 +1196,18 @@ class Shape : public gc::BarrieredCell<S
 
     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().
+        AutoThreadSafeAccess ts(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);
     }
@@ -1200,36 +1219,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 {
+        AutoThreadSafeAccess ts(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 {
+        // Return the actual jsid, not an internal reference.
+        AutoThreadSafeAccess ts(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());
+        AutoThreadSafeAccess ts(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;
     }
@@ -1611,19 +1637,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; ) {
+        AutoThreadSafeAccess ts(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::AutoThreadSafeAccess ts(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::AutoThreadSafeAccess ts(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
@@ -319,19 +319,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) {
+        AutoThreadSafeAccess ts(tarr);
         return tarr->getFixedSlot(BYTELENGTH_SLOT);
     }
     static Value lengthValue(TypedArrayObject *tarr) {
+        AutoThreadSafeAccess ts(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();
@@ -339,19 +341,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 {
+        AutoThreadSafeAccess ts(this);
         return getFixedSlot(TYPE_SLOT).toInt32();
     }
     void *viewData() const {
+        AutoThreadSafeAccess ts(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(JSContext *cx);