Backed out changeset 3f861148c83c (bug 1191236) for hazard bustage
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 06 Aug 2015 13:04:18 +0200
changeset 288177 71263add546ecbb88c02303b365d43f950680a13
parent 288176 f5ed4dae7a9c702eef49c3de3f0fa9b8882e0a17
child 288178 cde7888b5ce1286354d87c5d2f5a94b50cc8c0f1
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1191236
milestone42.0a1
backs out3f861148c83c282f0ecbeb4e086cf84172f731e5
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
Backed out changeset 3f861148c83c (bug 1191236) for hazard bustage
js/public/TraceableHashTable.h
js/public/TraceableVector.h
js/public/Value.h
js/src/gc/Barrier.h
js/src/jsapi.h
js/src/jsscript.h
js/src/vm/Interpreter.cpp
js/src/vm/ObjectGroup.h
--- a/js/public/TraceableHashTable.h
+++ b/js/public/TraceableHashTable.h
@@ -71,17 +71,17 @@ class TraceableHashMapOperations
 {
     using Map = TraceableHashMap<Args...>;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
     using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
     using Enum = typename Map::Enum;
 
-    const Map& map() const { return static_cast<const Outer*>(this)->get(); }
+    const Map& map() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
     Range all() const                          { return map().all(); }
     bool empty() const                         { return map().empty(); }
     uint32_t count() const                     { return map().count(); }
@@ -96,17 +96,17 @@ class MutableTraceableHashMapOperations
 {
     using Map = TraceableHashMap<Args...>;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
     using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
     using Enum = typename Map::Enum;
 
-    Map& map() { return static_cast<Outer*>(this)->get(); }
+    Map& map() { return static_cast<Outer*>(this)->extract(); }
 
   public:
     bool init(uint32_t len = 16) { return map().init(len); }
     void clear()                 { map().clear(); }
     void finish()                { map().finish(); }
     void remove(Ptr p)           { map().remove(p); }
 
     template<typename KeyInput, typename ValueInput>
@@ -135,24 +135,46 @@ class MutableTraceableHashMapOperations
     bool putNew(KeyInput&& k, ValueInput&& v) {
         return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     }
 };
 
 template <typename A, typename B, typename C, typename D, typename E, typename F>
 class RootedBase<TraceableHashMap<A,B,C,D,E,F>>
   : public MutableTraceableHashMapOperations<JS::Rooted<TraceableHashMap<A,B,C,D,E,F>>, A,B,C,D,E,F>
-{};
+{
+    using Map = TraceableHashMap<A,B,C,D,E,F>;
+
+    friend class TraceableHashMapOperations<JS::Rooted<Map>, A,B,C,D,E,F>;
+    const Map& extract() const { return *static_cast<const JS::Rooted<Map>*>(this)->address(); }
+
+    friend class MutableTraceableHashMapOperations<JS::Rooted<Map>, A,B,C,D,E,F>;
+    Map& extract() { return *static_cast<JS::Rooted<Map>*>(this)->address(); }
+};
 
 template <typename A, typename B, typename C, typename D, typename E, typename F>
 class MutableHandleBase<TraceableHashMap<A,B,C,D,E,F>>
   : public MutableTraceableHashMapOperations<JS::MutableHandle<TraceableHashMap<A,B,C,D,E,F>>,
                                              A,B,C,D,E,F>
-{};
+{
+    using Map = TraceableHashMap<A,B,C,D,E,F>;
+
+    friend class TraceableHashMapOperations<JS::MutableHandle<Map>, A,B,C,D,E,F>;
+    const Map& extract() const {
+        return *static_cast<const JS::MutableHandle<Map>*>(this)->address();
+    }
+
+    friend class MutableTraceableHashMapOperations<JS::MutableHandle<Map>, A,B,C,D,E,F>;
+    Map& extract() { return *static_cast<JS::MutableHandle<Map>*>(this)->address(); }
+};
 
 template <typename A, typename B, typename C, typename D, typename E, typename F>
 class HandleBase<TraceableHashMap<A,B,C,D,E,F>>
   : public TraceableHashMapOperations<JS::Handle<TraceableHashMap<A,B,C,D,E,F>>, A,B,C,D,E,F>
-{};
+{
+    using Map = TraceableHashMap<A,B,C,D,E,F>;
+    friend class TraceableHashMapOperations<JS::Handle<Map>, A,B,C,D,E,F>;
+    const Map& extract() const { return *static_cast<const JS::Handle<Map>*>(this)->address(); }
+};
 
 } /* namespace js */
 
 #endif /* gc_HashTable_h */
--- a/js/public/TraceableVector.h
+++ b/js/public/TraceableVector.h
@@ -54,17 +54,17 @@ class TraceableVector
             TraceFunc::trace(trc, &Base::operator[](i), "vector element");
     }
 };
 
 template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
 class TraceableVectorOperations
 {
     using Vec = TraceableVector<T, Capacity, AllocPolicy, TraceFunc>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     size_t length() const { return vec().length(); }
     bool empty() const { return vec().empty(); }
     size_t capacity() const { return vec().capacity(); }
     const T* begin() const { return vec().begin(); }
     const T* end() const { return vec().end(); }
@@ -76,18 +76,18 @@ class TraceableVectorOperations
     }
 };
 
 template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
 class MutableTraceableVectorOperations
   : public TraceableVectorOperations<Outer, T, Capacity, AllocPolicy, TraceFunc>
 {
     using Vec = TraceableVector<T, Capacity, AllocPolicy, TraceFunc>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
-    Vec& vec() { return static_cast<Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Outer*>(this)->extract(); }
+    Vec& vec() { return static_cast<Outer*>(this)->extract(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     AllocPolicy& allocPolicy() { return vec().allocPolicy(); }
     const T* begin() const { return vec().begin(); }
     T* begin() { return vec().begin(); }
     const T* end() const { return vec().end(); }
     T* end() { return vec().end(); }
@@ -139,23 +139,48 @@ class MutableTraceableVectorOperations
     }
     void erase(T* aT) { vec().erase(aT); }
     void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
 };
 
 template <typename A, size_t B, typename C, typename D>
 class RootedBase<TraceableVector<A,B,C,D>>
   : public MutableTraceableVectorOperations<JS::Rooted<TraceableVector<A,B,C,D>>, A,B,C,D>
-{};
+{
+    using Vec = TraceableVector<A,B,C,D>;
+
+    friend class TraceableVectorOperations<JS::Rooted<Vec>, A,B,C,D>;
+    const Vec& extract() const { return *static_cast<const JS::Rooted<Vec>*>(this)->address(); }
+
+    friend class MutableTraceableVectorOperations<JS::Rooted<Vec>, A,B,C,D>;
+    Vec& extract() { return *static_cast<JS::Rooted<Vec>*>(this)->address(); }
+};
 
 template <typename A, size_t B, typename C, typename D>
 class MutableHandleBase<TraceableVector<A,B,C,D>>
   : public MutableTraceableVectorOperations<JS::MutableHandle<TraceableVector<A,B,C,D>>, A,B,C,D>
-{};
+{
+    using Vec = TraceableVector<A,B,C,D>;
+
+    friend class TraceableVectorOperations<JS::MutableHandle<Vec>, A,B,C,D>;
+    const Vec& extract() const {
+        return *static_cast<const JS::MutableHandle<Vec>*>(this)->address();
+    }
+
+    friend class MutableTraceableVectorOperations<JS::MutableHandle<Vec>, A,B,C,D>;
+    Vec& extract() { return *static_cast<JS::MutableHandle<Vec>*>(this)->address(); }
+};
 
 template <typename A, size_t B, typename C, typename D>
 class HandleBase<TraceableVector<A,B,C,D>>
   : public TraceableVectorOperations<JS::Handle<TraceableVector<A,B,C,D>>, A,B,C,D>
-{};
+{
+    using Vec = TraceableVector<A,B,C,D>;
+
+    friend class TraceableVectorOperations<JS::Handle<Vec>, A,B,C,D>;
+    const Vec& extract() const {
+        return *static_cast<const JS::Handle<Vec>*>(this)->address();
+    }
+};
 
 } // namespace js
 
 #endif // js_TraceableVector_h
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1671,105 +1671,107 @@ template <> struct GCMethods<JS::Value>
     }
 };
 
 template <class Outer> class MutableValueOperations;
 
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
- * ValueOperations<Outer> with a visible get() method returning a const
- * reference to the Value abstracted by Outer.
+ * ValueOperations<Outer> with a visible extract() method returning the
+ * const Value* abstracted by Outer.
  */
 template <class Outer>
 class ValueOperations
 {
     friend class MutableValueOperations<Outer>;
 
-    const JS::Value& value() const { return static_cast<const Outer*>(this)->get(); }
+    const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
-    bool isUndefined() const { return value().isUndefined(); }
-    bool isNull() const { return value().isNull(); }
-    bool isBoolean() const { return value().isBoolean(); }
-    bool isTrue() const { return value().isTrue(); }
-    bool isFalse() const { return value().isFalse(); }
-    bool isNumber() const { return value().isNumber(); }
-    bool isInt32() const { return value().isInt32(); }
-    bool isInt32(int32_t i32) const { return value().isInt32(i32); }
-    bool isDouble() const { return value().isDouble(); }
-    bool isString() const { return value().isString(); }
-    bool isSymbol() const { return value().isSymbol(); }
-    bool isObject() const { return value().isObject(); }
-    bool isMagic() const { return value().isMagic(); }
-    bool isMagic(JSWhyMagic why) const { return value().isMagic(why); }
-    bool isMarkable() const { return value().isMarkable(); }
-    bool isPrimitive() const { return value().isPrimitive(); }
-    bool isGCThing() const { return value().isGCThing(); }
+    bool isUndefined() const { return value()->isUndefined(); }
+    bool isNull() const { return value()->isNull(); }
+    bool isBoolean() const { return value()->isBoolean(); }
+    bool isTrue() const { return value()->isTrue(); }
+    bool isFalse() const { return value()->isFalse(); }
+    bool isNumber() const { return value()->isNumber(); }
+    bool isInt32() const { return value()->isInt32(); }
+    bool isInt32(int32_t i32) const { return value()->isInt32(i32); }
+    bool isDouble() const { return value()->isDouble(); }
+    bool isString() const { return value()->isString(); }
+    bool isSymbol() const { return value()->isSymbol(); }
+    bool isObject() const { return value()->isObject(); }
+    bool isMagic() const { return value()->isMagic(); }
+    bool isMagic(JSWhyMagic why) const { return value()->isMagic(why); }
+    bool isMarkable() const { return value()->isMarkable(); }
+    bool isPrimitive() const { return value()->isPrimitive(); }
+    bool isGCThing() const { return value()->isGCThing(); }
 
-    bool isNullOrUndefined() const { return value().isNullOrUndefined(); }
-    bool isObjectOrNull() const { return value().isObjectOrNull(); }
+    bool isNullOrUndefined() const { return value()->isNullOrUndefined(); }
+    bool isObjectOrNull() const { return value()->isObjectOrNull(); }
 
-    bool toBoolean() const { return value().toBoolean(); }
-    double toNumber() const { return value().toNumber(); }
-    int32_t toInt32() const { return value().toInt32(); }
-    double toDouble() const { return value().toDouble(); }
-    JSString* toString() const { return value().toString(); }
-    JS::Symbol* toSymbol() const { return value().toSymbol(); }
-    JSObject& toObject() const { return value().toObject(); }
-    JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
-    gc::Cell* toGCThing() const { return value().toGCThing(); }
-    JS::TraceKind traceKind() const { return value().traceKind(); }
-    uint64_t asRawBits() const { return value().asRawBits(); }
+    bool toBoolean() const { return value()->toBoolean(); }
+    double toNumber() const { return value()->toNumber(); }
+    int32_t toInt32() const { return value()->toInt32(); }
+    double toDouble() const { return value()->toDouble(); }
+    JSString* toString() const { return value()->toString(); }
+    JS::Symbol* toSymbol() const { return value()->toSymbol(); }
+    JSObject& toObject() const { return value()->toObject(); }
+    JSObject* toObjectOrNull() const { return value()->toObjectOrNull(); }
+    gc::Cell* toGCThing() const { return value()->toGCThing(); }
+    JS::TraceKind traceKind() const { return value()->traceKind(); }
+    uint64_t asRawBits() const { return value()->asRawBits(); }
 
-    JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
-    uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
+    JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); }
+    uint32_t toPrivateUint32() const { return value()->toPrivateUint32(); }
 
-    JSWhyMagic whyMagic() const { return value().whyMagic(); }
-    uint32_t magicUint32() const { return value().magicUint32(); }
+    JSWhyMagic whyMagic() const { return value()->whyMagic(); }
+    uint32_t magicUint32() const { return value()->magicUint32(); }
 };
 
 /*
  * A class designed for CRTP use in implementing all the mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations<Outer> with visible get() methods returning const and
- * non-const references to the Value abstracted by Outer.
+ * MutableValueOperations<Outer> with visible extractMutable() and extract()
+ * methods returning the const Value* and Value* abstracted by Outer.
  */
 template <class Outer>
 class MutableValueOperations : public ValueOperations<Outer>
 {
-    JS::Value& value() { return static_cast<Outer*>(this)->get(); }
+    JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
-    void setNull() { value().setNull(); }
-    void setUndefined() { value().setUndefined(); }
-    void setInt32(int32_t i) { value().setInt32(i); }
-    void setDouble(double d) { value().setDouble(d); }
+    void setNull() { value()->setNull(); }
+    void setUndefined() { value()->setUndefined(); }
+    void setInt32(int32_t i) { value()->setInt32(i); }
+    void setDouble(double d) { value()->setDouble(d); }
     void setNaN() { setDouble(JS::GenericNaN()); }
-    void setBoolean(bool b) { value().setBoolean(b); }
-    void setMagic(JSWhyMagic why) { value().setMagic(why); }
-    bool setNumber(uint32_t ui) { return value().setNumber(ui); }
-    bool setNumber(double d) { return value().setNumber(d); }
-    void setString(JSString* str) { this->value().setString(str); }
-    void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
-    void setObject(JSObject& obj) { this->value().setObject(obj); }
-    void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
+    void setBoolean(bool b) { value()->setBoolean(b); }
+    void setMagic(JSWhyMagic why) { value()->setMagic(why); }
+    bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
+    bool setNumber(double d) { return value()->setNumber(d); }
+    void setString(JSString* str) { this->value()->setString(str); }
+    void setSymbol(JS::Symbol* sym) { this->value()->setSymbol(sym); }
+    void setObject(JSObject& obj) { this->value()->setObject(obj); }
+    void setObjectOrNull(JSObject* arg) { this->value()->setObjectOrNull(arg); }
 };
 
 /*
  * Augment the generic Heap<T> interface when T = Value with
  * type-querying, value-extracting, and mutating operations.
  */
 template <>
 class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
 {
     typedef JS::Heap<JS::Value> Outer;
 
     friend class ValueOperations<Outer>;
 
+    const JS::Value * extract() const { return static_cast<const Outer*>(this)->address(); }
+
     void setBarriered(const JS::Value& v) {
         *static_cast<JS::Heap<JS::Value>*>(this) = v;
     }
 
   public:
     void setNull() { setBarriered(JS::NullValue()); }
     void setUndefined() { setBarriered(JS::UndefinedValue()); }
     void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
@@ -1805,31 +1807,82 @@ class HeapBase<JS::Value> : public Value
     void setObjectOrNull(JSObject* arg) {
         if (arg)
             setObject(*arg);
         else
             setNull();
     }
 };
 
+/*
+ * Augment the generic Handle<T> interface when T = Value with type-querying
+ * and value-extracting operations.
+ */
 template <>
 class HandleBase<JS::Value> : public ValueOperations<JS::Handle<JS::Value> >
-{};
+{
+    friend class ValueOperations<JS::Handle<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const JS::Handle<JS::Value>*>(this)->address();
+    }
+};
 
+/*
+ * Augment the generic MutableHandle<T> interface when T = Value with
+ * type-querying, value-extracting, and mutating operations.
+ */
 template <>
 class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
-{};
+{
+    friend class ValueOperations<JS::MutableHandle<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const JS::MutableHandle<JS::Value>*>(this)->address();
+    }
 
+    friend class MutableValueOperations<JS::MutableHandle<JS::Value> >;
+    JS::Value * extractMutable() {
+        return static_cast<JS::MutableHandle<JS::Value>*>(this)->address();
+    }
+};
+
+/*
+ * Augment the generic Rooted<T> interface when T = Value with type-querying,
+ * value-extracting, and mutating operations.
+ */
 template <>
 class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
-{};
+{
+    friend class ValueOperations<JS::Rooted<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const JS::Rooted<JS::Value>*>(this)->address();
+    }
 
+    friend class MutableValueOperations<JS::Rooted<JS::Value> >;
+    JS::Value * extractMutable() {
+        return static_cast<JS::Rooted<JS::Value>*>(this)->address();
+    }
+};
+
+/*
+ * Augment the generic PersistentRooted<T> interface when T = Value with type-querying,
+ * value-extracting, and mutating operations.
+ */
 template <>
 class PersistentRootedBase<JS::Value> : public MutableValueOperations<JS::PersistentRooted<JS::Value>>
-{};
+{
+    friend class ValueOperations<JS::PersistentRooted<JS::Value>>;
+    const JS::Value * extract() const {
+        return static_cast<const JS::PersistentRooted<JS::Value>*>(this)->address();
+    }
+
+    friend class MutableValueOperations<JS::PersistentRooted<JS::Value>>;
+    JS::Value * extractMutable() {
+        return static_cast<JS::PersistentRooted<JS::Value>*>(this)->address();
+    }
+};
 
 /*
  * If the Value is a GC pointer type, convert to that type and call |f| with
  * the pointer. If the Value is not a GC type, calls F::defaultValue.
  */
 template <typename F, typename... Args>
 auto
 DispatchValueTyped(F f, const JS::Value& val, Args&&... args)
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -337,17 +337,22 @@ class BarrieredBase : public BarrieredBa
     static void writeBarrierPre(const T& v) { InternalGCMethods<T>::preBarrier(v); }
 
   protected:
     void pre() { InternalGCMethods<T>::preBarrier(value); }
 };
 
 template <>
 class BarrieredBaseMixins<JS::Value> : public ValueOperations<BarrieredBase<JS::Value> >
-{};
+{
+    friend class ValueOperations<BarrieredBase<JS::Value> >;
+    const JS::Value * extract() const {
+        return static_cast<const BarrieredBase<JS::Value>*>(this)->unsafeGet();
+    }
+};
 
 /*
  * PreBarriered only automatically handles pre-barriers. Post-barriers must
  * be manually implemented when using this class. HeapPtr and RelocatablePtr
  * should be used in all cases that do not require explicit low-level control
  * of moving behavior, e.g. for HashMap keys.
  */
 template <class T>
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2522,82 +2522,82 @@ struct JSPropertyDescriptor : public JS:
     void trace(JSTracer* trc);
 };
 
 namespace JS {
 
 template <typename Outer>
 class PropertyDescriptorOperations
 {
-    const JSPropertyDescriptor& desc() const { return static_cast<const Outer*>(this)->get(); }
+    const JSPropertyDescriptor* desc() const { return static_cast<const Outer*>(this)->extract(); }
 
     bool has(unsigned bit) const {
         MOZ_ASSERT(bit != 0);
         MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
-        return (desc().attrs & bit) != 0;
+        return (desc()->attrs & bit) != 0;
     }
 
     bool hasAny(unsigned bits) const {
-        return (desc().attrs & bits) != 0;
+        return (desc()->attrs & bits) != 0;
     }
 
     bool hasAll(unsigned bits) const {
-        return (desc().attrs & bits) == bits;
+        return (desc()->attrs & bits) == bits;
     }
 
     // Non-API attributes bit used internally for arguments objects.
     enum { SHADOWABLE = JSPROP_INTERNAL_USE_BIT };
 
   public:
     // Descriptors with JSGetterOp/JSSetterOp are considered data
     // descriptors. It's complicated.
     bool isAccessorDescriptor() const { return hasAny(JSPROP_GETTER | JSPROP_SETTER); }
     bool isGenericDescriptor() const {
-        return (desc().attrs&
+        return (desc()->attrs&
                 (JSPROP_GETTER | JSPROP_SETTER | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
                (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
     }
     bool isDataDescriptor() const { return !isAccessorDescriptor() && !isGenericDescriptor(); }
 
     bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
     bool configurable() const { MOZ_ASSERT(hasConfigurable()); return !has(JSPROP_PERMANENT); }
 
     bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
     bool enumerable() const { MOZ_ASSERT(hasEnumerable()); return has(JSPROP_ENUMERATE); }
 
     bool hasValue() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE); }
     JS::HandleValue value() const {
-        return JS::HandleValue::fromMarkedLocation(&desc().value);
+        return JS::HandleValue::fromMarkedLocation(&desc()->value);
     }
 
     bool hasWritable() const { return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY); }
     bool writable() const { MOZ_ASSERT(hasWritable()); return !has(JSPROP_READONLY); }
 
     bool hasGetterObject() const { return has(JSPROP_GETTER); }
     JS::HandleObject getterObject() const {
         MOZ_ASSERT(hasGetterObject());
         return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject* const*>(&desc().getter));
+                reinterpret_cast<JSObject* const*>(&desc()->getter));
     }
     bool hasSetterObject() const { return has(JSPROP_SETTER); }
     JS::HandleObject setterObject() const {
         MOZ_ASSERT(hasSetterObject());
         return JS::HandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject* const*>(&desc().setter));
-    }
-
-    bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
+                reinterpret_cast<JSObject* const*>(&desc()->setter));
+    }
+
+    bool hasGetterOrSetter() const { return desc()->getter || desc()->setter; }
     bool isShared() const { return has(JSPROP_SHARED); }
 
     JS::HandleObject object() const {
-        return JS::HandleObject::fromMarkedLocation(&desc().obj);
-    }
-    unsigned attributes() const { return desc().attrs; }
-    JSGetterOp getter() const { return desc().getter; }
-    JSSetterOp setter() const { return desc().setter; }
+        return JS::HandleObject::fromMarkedLocation(&desc()->obj);
+    }
+    unsigned attributes() const { return desc()->attrs; }
+    JSGetterOp getter() const { return desc()->getter; }
+    JSSetterOp setter() const { return desc()->setter; }
 
     void assertValid() const {
 #ifdef DEBUG
         MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE |
                                      JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT |
                                      JSPROP_READONLY | JSPROP_IGNORE_READONLY |
                                      JSPROP_IGNORE_VALUE |
                                      JSPROP_GETTER |
@@ -2654,17 +2654,17 @@ class PropertyDescriptorOperations
             assertComplete();
 #endif
     }
 };
 
 template <typename Outer>
 class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<Outer>
 {
-    JSPropertyDescriptor& desc() { return static_cast<Outer*>(this)->get(); }
+    JSPropertyDescriptor * desc() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
     void clear() {
         object().set(nullptr);
         setAttributes(0);
         setGetter(nullptr);
         setSetter(nullptr);
         value().setUndefined();
@@ -2700,94 +2700,117 @@ class MutablePropertyDescriptorOperation
         object().set(nullptr);
         setAttributes(attrs);
         setGetter(nullptr);
         setSetter(nullptr);
         value().set(v);
     }
 
     JS::MutableHandleObject object() {
-        return JS::MutableHandleObject::fromMarkedLocation(&desc().obj);
-    }
-    unsigned& attributesRef() { return desc().attrs; }
-    JSGetterOp& getter() { return desc().getter; }
-    JSSetterOp& setter() { return desc().setter; }
+        return JS::MutableHandleObject::fromMarkedLocation(&desc()->obj);
+    }
+    unsigned& attributesRef() { return desc()->attrs; }
+    JSGetterOp& getter() { return desc()->getter; }
+    JSSetterOp& setter() { return desc()->setter; }
     JS::MutableHandleValue value() {
-        return JS::MutableHandleValue::fromMarkedLocation(&desc().value);
+        return JS::MutableHandleValue::fromMarkedLocation(&desc()->value);
     }
     void setValue(JS::HandleValue v) {
-        MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         attributesRef() &= ~JSPROP_IGNORE_VALUE;
         value().set(v);
     }
 
     void setConfigurable(bool configurable) {
-        setAttributes((desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
+        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
                       (configurable ? 0 : JSPROP_PERMANENT));
     }
     void setEnumerable(bool enumerable) {
-        setAttributes((desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
+        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
                       (enumerable ? JSPROP_ENUMERATE : 0));
     }
     void setWritable(bool writable) {
-        MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-        setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
+        MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+        setAttributes((desc()->attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
                       (writable ? 0 : JSPROP_READONLY));
     }
-    void setAttributes(unsigned attrs) { desc().attrs = attrs; }
+    void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
 
     void setGetter(JSGetterOp op) {
         MOZ_ASSERT(op != JS_PropertyStub);
-        desc().getter = op;
+        desc()->getter = op;
     }
     void setSetter(JSSetterOp op) {
         MOZ_ASSERT(op != JS_StrictPropertyStub);
-        desc().setter = op;
+        desc()->setter = op;
     }
     void setGetterObject(JSObject* obj) {
-        desc().getter = reinterpret_cast<JSGetterOp>(obj);
-        desc().attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-        desc().attrs |= JSPROP_GETTER | JSPROP_SHARED;
+        desc()->getter = reinterpret_cast<JSGetterOp>(obj);
+        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+        desc()->attrs |= JSPROP_GETTER | JSPROP_SHARED;
     }
     void setSetterObject(JSObject* obj) {
-        desc().setter = reinterpret_cast<JSSetterOp>(obj);
-        desc().attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
-        desc().attrs |= JSPROP_SETTER | JSPROP_SHARED;
+        desc()->setter = reinterpret_cast<JSSetterOp>(obj);
+        desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
+        desc()->attrs |= JSPROP_SETTER | JSPROP_SHARED;
     }
 
     JS::MutableHandleObject getterObject() {
         MOZ_ASSERT(this->hasGetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject**>(&desc().getter));
+                reinterpret_cast<JSObject**>(&desc()->getter));
     }
     JS::MutableHandleObject setterObject() {
         MOZ_ASSERT(this->hasSetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
-                reinterpret_cast<JSObject**>(&desc().setter));
+                reinterpret_cast<JSObject**>(&desc()->setter));
     }
 };
 
 } /* namespace JS */
 
 namespace js {
 
 template <>
 class RootedBase<JSPropertyDescriptor>
   : public JS::MutablePropertyDescriptorOperations<JS::Rooted<JSPropertyDescriptor>>
-{};
+{
+    friend class JS::PropertyDescriptorOperations<JS::Rooted<JSPropertyDescriptor>>;
+    friend class JS::MutablePropertyDescriptorOperations<JS::Rooted<JSPropertyDescriptor>>;
+    const JSPropertyDescriptor* extract() const {
+        return static_cast<const JS::Rooted<JSPropertyDescriptor>*>(this)->address();
+    }
+    JSPropertyDescriptor* extractMutable() {
+        return static_cast<JS::Rooted<JSPropertyDescriptor>*>(this)->address();
+    }
+};
 
 template <>
 class HandleBase<JSPropertyDescriptor>
   : public JS::PropertyDescriptorOperations<JS::Handle<JSPropertyDescriptor>>
-{};
+{
+    friend class JS::PropertyDescriptorOperations<JS::Handle<JSPropertyDescriptor>>;
+    const JSPropertyDescriptor* extract() const {
+        return static_cast<const JS::Handle<JSPropertyDescriptor>*>(this)->address();
+    }
+};
 
 template <>
 class MutableHandleBase<JSPropertyDescriptor>
   : public JS::MutablePropertyDescriptorOperations<JS::MutableHandle<JSPropertyDescriptor>>
-{};
+{
+    friend class JS::PropertyDescriptorOperations<JS::MutableHandle<JSPropertyDescriptor>>;
+    friend class JS::MutablePropertyDescriptorOperations<JS::MutableHandle<JSPropertyDescriptor>>;
+    const JSPropertyDescriptor* extract() const {
+        return static_cast<const JS::MutableHandle<JSPropertyDescriptor>*>(this)->address();
+    }
+    JSPropertyDescriptor* extractMutable() {
+        return static_cast<JS::MutableHandle<JSPropertyDescriptor>*>(this)->address();
+    }
+};
 
 } /* namespace js */
 
 namespace JS {
 
 extern JS_PUBLIC_API(bool)
 ObjectToCompletePropertyDescriptor(JSContext* cx,
                                    JS::HandleObject obj,
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -342,17 +342,17 @@ class Bindings : public JS::Traceable
 
     static void trace(Bindings* self, JSTracer* trc) { self->trace(trc); }
     void trace(JSTracer* trc);
 };
 
 template <class Outer>
 class BindingsOperations
 {
-    const Bindings& bindings() const { return static_cast<const Outer*>(this)->get(); }
+    const Bindings& bindings() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     // Direct data access to the underlying bindings.
     const RelocatablePtrShape& callObjShape() const {
         return bindings().callObjShape_;
     }
     uint16_t numArgs() const {
         return bindings().numArgs_;
@@ -406,17 +406,17 @@ class BindingsOperations
     uint32_t lexicalBegin() const {
         return numArgs() + numVars();
     }
 };
 
 template <class Outer>
 class MutableBindingsOperations : public BindingsOperations<Outer>
 {
-    Bindings& bindings() { return static_cast<Outer*>(this)->get(); }
+    Bindings& bindings() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
     void setCallObjShape(HandleShape shape) { bindings().callObjShape_ = shape; }
     void setBindingArray(const Binding* bindingArray, uintptr_t temporaryBit) {
         bindings().bindingArrayAndFlag_ = uintptr_t(bindingArray) | temporaryBit;
     }
     void setNumArgs(uint16_t num) { bindings().numArgs_ = num; }
     void setNumVars(uint32_t num) { bindings().numVars_ = num; }
@@ -431,22 +431,37 @@ class MutableBindingsOperations : public
     }
     uint8_t* switchToScriptStorage(Binding* permanentStorage) {
         return bindings().switchToScriptStorage(permanentStorage);
     }
 };
 
 template <>
 class HandleBase<Bindings> : public BindingsOperations<JS::Handle<Bindings>>
-{};
+{
+    friend class BindingsOperations<JS::Handle<Bindings>>;
+    const Bindings& extract() const {
+        return static_cast<const JS::Handle<Bindings>*>(this)->get();
+    }
+};
 
 template <>
 class MutableHandleBase<Bindings>
   : public MutableBindingsOperations<JS::MutableHandle<Bindings>>
-{};
+{
+    friend class BindingsOperations<JS::MutableHandle<Bindings>>;
+    const Bindings& extract() const {
+        return static_cast<const JS::MutableHandle<Bindings>*>(this)->get();
+    }
+
+    friend class MutableBindingsOperations<JS::MutableHandle<Bindings>>;
+    Bindings& extractMutable() {
+        return static_cast<JS::MutableHandle<Bindings>*>(this)->get();
+    }
+};
 
 class ScriptCounts
 {
     friend class ::JSScript;
     friend struct ScriptAndCounts;
 
     /*
      * This points to a single block that holds an array of PCCounts followed
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1780,17 +1780,22 @@ class ReservedRooted : public ReservedRo
     DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_POINTER_CONSTREF_OPS(T)
     DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
 };
 
 template <>
 class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
-{};
+{
+    friend class ValueOperations<ReservedRooted<Value>>;
+    const Value* extract() const {
+        return static_cast<const ReservedRooted<Value>*>(this)->address();
+    }
+};
 
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
  * on compiler support.
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -76,36 +76,46 @@ template <> struct GCMethods<const Tagge
 template <> struct GCMethods<TaggedProto>
 {
     static TaggedProto initial() { return TaggedProto(); }
 };
 
 template<class Outer>
 class TaggedProtoOperations
 {
-    const TaggedProto& value() const {
-        return static_cast<const Outer*>(this)->get();
+    const TaggedProto* value() const {
+        return static_cast<const Outer*>(this)->extract();
     }
 
   public:
-    uintptr_t toWord() const { return value().toWord(); }
-    inline bool isLazy() const { return value().isLazy(); }
-    inline bool isObject() const { return value().isObject(); }
-    inline JSObject* toObject() const { return value().toObject(); }
-    inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
-    JSObject* raw() const { return value().raw(); }
+    uintptr_t toWord() const { return value()->toWord(); }
+    inline bool isLazy() const { return value()->isLazy(); }
+    inline bool isObject() const { return value()->isObject(); }
+    inline JSObject* toObject() const { return value()->toObject(); }
+    inline JSObject* toObjectOrNull() const { return value()->toObjectOrNull(); }
+    JSObject* raw() const { return value()->raw(); }
 };
 
 template <>
 class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto> >
-{};
+{
+    friend class TaggedProtoOperations<Handle<TaggedProto> >;
+    const TaggedProto * extract() const {
+        return static_cast<const Handle<TaggedProto>*>(this)->address();
+    }
+};
 
 template <>
 class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto> >
-{};
+{
+    friend class TaggedProtoOperations<Rooted<TaggedProto> >;
+    const TaggedProto* extract() const {
+        return static_cast<const Rooted<TaggedProto>*>(this)->address();
+    }
+};
 
 // Since JSObject pointers are either nullptr or a valid object and since the
 // object layout of TaggedProto is identical to a bare object pointer, we can
 // safely treat a pointer to an already-rooted object (e.g. HandleObject) as a
 // pointer to a TaggedProto.
 inline Handle<TaggedProto>
 AsTaggedProto(HandleObject obj)
 {