Bug 761695 - Unify holder creation and access. r=peterv
authorBobby Holley <bobbyholley@gmail.com>
Fri, 05 Oct 2012 18:59:23 +0200
changeset 115714 b1c000f475d45cb532e567d7a099b80cc2597fa4
parent 115713 03667c5bc77b2eea5e3faf521282ee0d30486716
child 115715 455e5bd1cd2a48e06c7c4fcb3529e860e8d81141
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs761695
milestone18.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 761695 - Unify holder creation and access. r=peterv With this patch, all holders are created lazily. There are two common accessors, getHolder() and ensureHolder(). The former returns null if no holder exists, the latter lazily creates the holder if it doesn't exist. It does this by calling into a virtual trap on XrayTraits, which lets the appropriate Xray type do its thing.
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -324,17 +324,16 @@ WrapperFactory::Rewrap(JSContext *cx, JS
     NS_ASSERTION(!IsWrapper(obj) ||
                  GetProxyHandler(obj) == &XrayWaiver ||
                  js::GetObjectClass(obj)->ext.innerObject,
                  "wrapped object passed to rewrap");
     NS_ASSERTION(JS_GetClass(obj) != &XrayUtils::HolderClass, "trying to wrap a holder");
 
     JSCompartment *origin = js::GetObjectCompartment(obj);
     JSCompartment *target = js::GetContextCompartment(cx);
-    bool usingXray = false;
 
     // By default we use the wrapped proto of the underlying object as the
     // prototype for our wrapper, but we may select something different below.
     JSObject *proxyProto = wrappedProto;
 
     Wrapper *wrapper;
     CompartmentPrivate *targetdata = GetCompartmentPrivate(target);
     if (AccessCheck::isChrome(target)) {
@@ -349,17 +348,16 @@ WrapperFactory::Rewrap(JSContext *cx, JS
                 // Native objects must be wrapped into an X-ray wrapper.
                 XrayType type = GetXrayType(obj);
                 if (type == XrayForDOMObject) {
                     wrapper = &XrayDOM::singleton;
                 } else if (type == XrayForDOMProxyObject) {
                     wrapper = &XrayProxy::singleton;
                 } else if (type == XrayForWrappedNative) {
                     typedef XrayWrapper<CrossCompartmentWrapper> Xray;
-                    usingXray = true;
                     wrapper = &Xray::singleton;
                 } else {
                     wrapper = &CrossCompartmentWrapper::singleton;
                 }
             }
         }
     } else if (xpc::IsUniversalXPConnectEnabled(target)) {
         wrapper = &CrossCompartmentWrapper::singleton;
@@ -372,17 +370,16 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             }
         }
 
         XPCWrappedNative *wn;
         if (targetdata &&
             (wn = GetWrappedNative(cx, obj)) &&
             wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) {
             typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
-            usingXray = true;
             if (IsLocationObject(obj))
                 wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             else
                 wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::IsDOMObject(obj)) {
             wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) {
             wrapper = &FilteringWrapper<XrayProxy, CrossOriginAccessiblePropertiesOnly>::singleton;
@@ -436,31 +433,29 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         // The first two cases always require a security wrapper for non-chrome
         // access, regardless of the origin of the object.
         XrayType type;
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
         } else if (IsLocationObject(obj)) {
             typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
-            usingXray = true;
             wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
         } else if (IsComponentsObject(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ComponentsObjectPolicy>::singleton;
         } else if (!targetdata || !targetdata->wantXrays ||
                    (type = GetXrayType(obj)) == NotXray) {
             wrapper = &CrossCompartmentWrapper::singleton;
         } else if (type == XrayForDOMObject) {
             wrapper = &XrayDOM::singleton;
         } else if (type == XrayForDOMProxyObject) {
             wrapper = &XrayProxy::singleton;
         } else {
             typedef XrayWrapper<CrossCompartmentWrapper> Xray;
-            usingXray = true;
             wrapper = &Xray::singleton;
         }
     } else {
         NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj),
                      "bad object exposed across origins");
 
         // Cross origin we want to disallow scripting and limit access to
         // a predefined set of properties.
@@ -471,38 +466,29 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         } else if (type == XrayForDOMObject) {
             wrapper = &FilteringWrapper<XrayDOM,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (type == XrayForDOMProxyObject) {
             wrapper = &FilteringWrapper<XrayProxy,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;
         } else {
             typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
-            usingXray = true;
 
             // Location objects can become same origin after navigation, so we might
             // have to grant transparent access later on.
             if (IsLocationObject(obj)) {
                 wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             } else {
                 wrapper = &FilteringWrapper<Xray,
                     CrossOriginAccessiblePropertiesOnly>::singleton;
             }
         }
     }
 
-    JSObject *wrapperObj = Wrapper::New(cx, obj, proxyProto, parent, wrapper);
-    if (!wrapperObj || !usingXray)
-        return wrapperObj;
-
-    JSObject *xrayHolder = XrayUtils::createHolder(cx, wrapperObj);
-    if (!xrayHolder)
-        return nullptr;
-    js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
-    return wrapperObj;
+    return Wrapper::New(cx, obj, proxyProto, parent, wrapper);
 }
 
 JSObject *
 WrapperFactory::WrapForSameCompartment(JSContext *cx, JSObject *obj)
 {
     // Only WNs have same-compartment wrappers.
     //
     // NB: The contract of WrapForSameCompartment says that |obj| may or may not
@@ -529,25 +515,17 @@ WrapperFactory::IsLocationObject(JSObjec
 }
 
 JSObject *
 WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj)
 {
     JSObject *proto;
     if (!js::GetObjectProto(cx, obj, &proto))
         return nullptr;
-    JSObject *wrapperObj = Wrapper::New(cx, obj, proto, js::GetObjectParent(obj),
-                                        &LW::singleton);
-    if (!wrapperObj)
-        return nullptr;
-    JSObject *xrayHolder = XrayUtils::createHolder(cx, wrapperObj);
-    if (!xrayHolder)
-        return nullptr;
-    js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
-    return wrapperObj;
+    return Wrapper::New(cx, obj, proto, js::GetObjectParent(obj), &LW::singleton);
 }
 
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp)
@@ -622,29 +600,17 @@ WrapperFactory::WrapForSameCompartmentXr
         wrapper = &XrayWrapper<DirectWrapper, ProxyXrayTraits>::singleton;
     else if (type == XrayForDOMObject)
         wrapper = &XrayWrapper<DirectWrapper, DOMXrayTraits>::singleton;
     else
         MOZ_NOT_REACHED("Bad Xray type");
 
     // Make the Xray.
     JSObject *parent = JS_GetGlobalForObject(cx, obj);
-    JSObject *wrapperObj = Wrapper::New(cx, obj, NULL, parent, wrapper);
-    if (!wrapperObj)
-        return NULL;
-
-    // Make the holder. Note that this is currently for WNs only until we fix
-    // bug 761704.
-    if (type == XrayForWrappedNative) {
-        JSObject *xrayHolder = XrayUtils::createHolder(cx, wrapperObj);
-        if (!xrayHolder)
-            return nullptr;
-        js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
-    }
-    return wrapperObj;
+    return Wrapper::New(cx, obj, NULL, parent, wrapper);
 }
 
 
 bool
 WrapperFactory::XrayWrapperNotShadowing(JSObject *wrapper, jsid id)
 {
     ResolvingId *rid = ResolvingId::getResolvingIdFromWrapper(wrapper);
     return rid->isXrayShadowing(id);
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -244,30 +244,16 @@ CloneExpandoChain(JSContext *cx, JSObjec
         JSObject *newHead =
           AttachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead), exclusive);
         if (!JS_CopyPropertiesFrom(cx, newHead, oldHead))
             return false;
         oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
     }
     return true;
 }
-
-JSObject *
-createHolder(JSContext *cx, JSObject *wrapper)
-{
-    JSObject *global = JS_GetGlobalForObject(cx, wrapper);
-    JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr,
-                                                  global);
-    if (!holder)
-        return nullptr;
-
-    js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(NULL));
-    return holder;
-}
-
 }
 
 using namespace XrayUtils;
 
 ResolvingId::ResolvingId(JSObject *wrapper, jsid id)
     : mId(id),
     mHolder(getHolderObject(wrapper)),
     mPrev(getResolvingId(mHolder)),
@@ -341,16 +327,21 @@ public:
     {
         MOZ_NOT_REACHED("Call trap currently implemented only for XPCWNs");
     }
     static bool construct(JSContext *cx, JSObject *wrapper, unsigned argc,
                           Value *argv, Value *rval)
     {
         MOZ_NOT_REACHED("Call trap currently implemented only for XPCWNs");
     }
+
+    JSObject* getHolder(JSObject *wrapper);
+    JSObject* ensureHolder(JSContext *cx, JSObject *wrapper);
+
+    virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0;
 };
 
 class XPCWrappedNativeXrayTraits : public XrayTraits
 {
 public:
     static bool resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id,
                                       bool set, JSPropertyDescriptor *desc);
     static bool resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper,
@@ -360,123 +351,82 @@ public:
                                JSPropertyDescriptor *desc);
     static bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     static bool enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                                JS::AutoIdVector &props);
     static bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp);
     static bool construct(JSContext *cx, JSObject *wrapper, unsigned argc,
                           Value *argv, Value *rval);
 
-    static JSObject* getHolderObject(JSContext *cx, JSObject *wrapper)
-    {
-        return getHolderObject(wrapper);
-    }
-
     static bool isResolving(JSContext *cx, JSObject *holder, jsid id);
 
     static bool resolveDOMCollectionProperty(JSContext *cx, JSObject *wrapper, JSObject *holder,
                                              jsid id, bool set, PropertyDescriptor *desc);
 
     static XPCWrappedNative* getWN(JSObject *wrapper) {
         return GetWrappedNative(getTargetObject(wrapper));
     }
 
     typedef ResolvingId ResolvingIdImpl;
 
-    static XPCWrappedNativeXrayTraits singleton;
+    virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
 
-private:
-    static JSObject* getHolderObject(JSObject *wrapper)
-    {
-        return &js::GetProxyExtra(wrapper, 0).toObject();
-    }
+    static XPCWrappedNativeXrayTraits singleton;
 };
 
 class ProxyXrayTraits : public XrayTraits
 {
 public:
     static bool resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id,
                                       bool set, JSPropertyDescriptor *desc);
     static bool resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper,
                                    JSObject *holder, jsid id, bool set,
                                    JSPropertyDescriptor *desc);
     static bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                JSPropertyDescriptor *desc);
     static bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     static bool enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                                JS::AutoIdVector &props);
-    static JSObject* getHolderObject(JSContext *cx, JSObject *wrapper)
-    {
-        return getHolderObject(cx, wrapper, true);
-    }
 
     static bool isResolving(JSContext *cx, JSObject *holder, jsid id)
     {
         return false;
     }
 
     typedef ResolvingIdDummy ResolvingIdImpl;
 
-    static ProxyXrayTraits singleton;
+    virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
 
-private:
-    static JSObject* getHolderObject(JSContext *cx, JSObject *wrapper,
-                                     bool createHolder)
-    {
-        if (!js::GetProxyExtra(wrapper, 0).isUndefined())
-            return &js::GetProxyExtra(wrapper, 0).toObject();
-
-        if (!createHolder)
-            return nullptr;
-
-        return createHolderObject(cx, wrapper);
-    }
-    static JSObject* createHolderObject(JSContext *cx, JSObject *wrapper);
+    static ProxyXrayTraits singleton;
 };
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     static bool resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id,
                                       bool set, JSPropertyDescriptor *desc);
     static bool resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper,
                                    JSObject *holder, jsid id, bool set,
                                    JSPropertyDescriptor *desc);
     static bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                JSPropertyDescriptor *desc);
     static bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     static bool enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                                JS::AutoIdVector &props);
-    static JSObject* getHolderObject(JSContext *cx, JSObject *wrapper)
-    {
-        return getHolderObject(cx, wrapper, true);
-    }
 
     static bool isResolving(JSContext *cx, JSObject *holder, jsid id)
     {
         return false;
     }
 
     typedef ResolvingIdDummy ResolvingIdImpl;
 
-    static DOMXrayTraits singleton;
+    virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
 
-private:
-    static JSObject* getHolderObject(JSContext *cx, JSObject *wrapper,
-                                     bool createHolder)
-    {
-        if (!js::GetProxyExtra(wrapper, 0).isUndefined())
-            return &js::GetProxyExtra(wrapper, 0).toObject();
-
-        if (!createHolder)
-            return nullptr;
-
-        return createHolderObject(cx, wrapper);
-    }
-    static JSObject* createHolderObject(JSContext *cx, JSObject *wrapper);
+    static DOMXrayTraits singleton;
 };
 
 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
 ProxyXrayTraits ProxyXrayTraits::singleton;
 DOMXrayTraits DOMXrayTraits::singleton;
 
 static JSObject *
 GetHolder(JSObject *obj)
@@ -505,16 +455,36 @@ FindWrapper(JSContext *cx, JSObject *wra
                 return nullptr;
         }
         // NB: we must eventually hit our wrapper.
     }
 
     return wrapper;
 }
 
+JSObject*
+XrayTraits::getHolder(JSObject *wrapper)
+{
+    MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
+    js::Value v = js::GetProxyExtra(wrapper, 0);
+    return v.isObject() ? &v.toObject() : nullptr;
+}
+
+JSObject*
+XrayTraits::ensureHolder(JSContext *cx, JSObject *wrapper)
+{
+    JSObject *holder = getHolder(wrapper);
+    if (holder)
+        return holder;
+    holder = createHolder(cx, wrapper); // virtual trap.
+    if (holder)
+        js::SetProxyExtra(wrapper, 0, ObjectValue(*holder));
+    return holder;
+}
+
 bool
 XPCWrappedNativeXrayTraits::isResolving(JSContext *cx, JSObject *holder,
                                         jsid id)
 {
     return ResolvingId::getResolvingId(holder)->isResolving(id);
 }
 
 // Some DOM objects have shared properties that don't have an explicit
@@ -995,17 +965,17 @@ XPCWrappedNativeXrayTraits::resolveOwnPr
 
     return true;
 }
 
 bool
 XPCWrappedNativeXrayTraits::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                       PropertyDescriptor *desc)
 {
-    JSObject *holder = getHolderObject(wrapper);
+    JSObject *holder = singleton.ensureHolder(cx, wrapper);
     if (isResolving(cx, holder, id)) {
         if (!(desc->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
             if (!desc->getter)
                 desc->getter = holder_get;
             if (!desc->setter)
                 desc->setter = holder_set;
         }
 
@@ -1084,16 +1054,29 @@ XPCWrappedNativeXrayTraits::enumerateNam
         if (!JS_HasPropertyById(cx, wrapper, id, &hasProp))
             return false;
         if (hasProp)
             props.append(id);
     }
     return true;
 }
 
+JSObject *
+XPCWrappedNativeXrayTraits::createHolder(JSContext *cx, JSObject *wrapper)
+{
+    JSObject *global = JS_GetGlobalForObject(cx, wrapper);
+    JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr,
+                                                  global);
+    if (!holder)
+        return nullptr;
+
+    js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(NULL));
+    return holder;
+}
+
 bool
 XPCWrappedNativeXrayTraits::call(JSContext *cx, JSObject *wrapper,
                                  unsigned argc, Value *vp)
 {
     // Run the resolve hook of the wrapped native.
     XPCWrappedNative *wn = getWN(wrapper);
     if (NATIVE_HAS_FLAG(wn, WantCall)) {
         XPCCallContext ccx(JS_CALLER, cx, wrapper, nullptr, JSID_VOID, argc,
@@ -1161,33 +1144,33 @@ ProxyXrayTraits::resolveOwnProperty(JSCo
     // Own properties don't get cached on the holder. Just return.
     return ok;
 }
 
 bool
 ProxyXrayTraits::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                 PropertyDescriptor *desc)
 {
-    JSObject *holder = getHolderObject(cx, wrapper);
+    JSObject *holder = singleton.ensureHolder(cx, wrapper);
     if (!holder)
         return false;
 
     return JS_DefinePropertyById(cx, holder, id, desc->value, desc->getter, desc->setter,
                                  desc->attrs);
 }
 
 bool
 ProxyXrayTraits::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     JSObject *obj = getTargetObject(wrapper);
     if (!js::GetProxyHandler(obj)->delete_(cx, wrapper, id, bp))
         return false;
 
     JSObject *holder;
-    if (*bp && (holder = getHolderObject(cx, wrapper, false)))
+    if (*bp && (holder = singleton.getHolder(wrapper)))
         JS_DeletePropertyById(cx, holder, id);
 
     return true;
 }
 
 bool
 ProxyXrayTraits::enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                                 JS::AutoIdVector &props)
@@ -1198,24 +1181,20 @@ ProxyXrayTraits::enumerateNames(JSContex
 
     return js::GetProxyHandler(obj)->enumerate(cx, wrapper, props);
 }
 
 // The 'holder' here isn't actually of [[Class]] HolderClass like those used by
 // XPCWrappedNativeXrayTraits. Instead, it's a funny hybrid of the 'expando' and
 // 'holder' properties. However, we store it in the same slot. Exercise caution.
 JSObject*
-ProxyXrayTraits::createHolderObject(JSContext *cx, JSObject *wrapper)
+ProxyXrayTraits::createHolder(JSContext *cx, JSObject *wrapper)
 {
-    JSObject *obj = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
-                                               JS_GetGlobalForObject(cx, wrapper));
-    if (!obj)
-        return nullptr;
-    js::SetProxyExtra(wrapper, 0, ObjectValue(*obj));
-    return obj;
+    return JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
+                                      JS_GetGlobalForObject(cx, wrapper));
 }
 
 bool
 DOMXrayTraits::resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id,
                                      bool set, JSPropertyDescriptor *desc)
 {
     JSObject *obj = getTargetObject(wrapper);
     const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
@@ -1249,29 +1228,29 @@ DOMXrayTraits::resolveOwnProperty(JSCont
     }
 
     return true;
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
 {
-    JSObject *holder = getHolderObject(cx, wrapper);
+    JSObject *holder = singleton.ensureHolder(cx, wrapper);
     if (!holder)
         return false;
 
     return JS_DefinePropertyById(cx, holder, id, desc->value, desc->getter, desc->setter,
                                  desc->attrs);
 }
 
 bool
 DOMXrayTraits::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
     JSObject *holder;
-    if ((holder = getHolderObject(cx, wrapper, false)))
+    if ((holder = singleton.getHolder(wrapper)))
         JS_DeletePropertyById(cx, holder, id);
 
     return true;
 }
 
 bool
 DOMXrayTraits::enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                               JS::AutoIdVector &props)
@@ -1292,24 +1271,20 @@ DOMXrayTraits::enumerateNames(JSContext 
             return false;
         }
     } while ((nativeHooks = nativeHooks->mProtoHooks));
 
     return true;
 }
 
 JSObject*
-DOMXrayTraits::createHolderObject(JSContext *cx, JSObject *wrapper)
+DOMXrayTraits::createHolder(JSContext *cx, JSObject *wrapper)
 {
-    JSObject *obj = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
-                                               JS_GetGlobalForObject(cx, wrapper));
-    if (!obj)
-        return nullptr;
-    js::SetProxyExtra(wrapper, 0, ObjectValue(*obj));
-    return obj;
+    return JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
+                                      JS_GetGlobalForObject(cx, wrapper));
 }
 
 template <typename Base, typename Traits>
 XrayWrapper<Base, Traits>::XrayWrapper(unsigned flags)
   : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG)
 {
 }
 
@@ -1388,17 +1363,17 @@ XrayToString(JSContext *cx, unsigned arg
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                  bool set, js::PropertyDescriptor *desc)
 {
-    JSObject *holder = Traits::getHolderObject(cx, wrapper);
+    JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper);
     if (Traits::isResolving(cx, holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     bool status;
     Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
     desc->obj = NULL; // default value
@@ -1488,17 +1463,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
            JS_GetPropertyDescriptorById(cx, holder, id, flags, desc);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                     bool set, PropertyDescriptor *desc)
 {
-    JSObject *holder = Traits::getHolderObject(cx, wrapper);
+    JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper);
     if (Traits::isResolving(cx, holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     bool status;
     Wrapper::Action action = set ? Wrapper::SET : Wrapper::GET;
     desc->obj = NULL; // default value
@@ -1545,17 +1520,17 @@ XrayWrapper<Base, Traits>::getOwnPropert
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                           js::PropertyDescriptor *desc)
 {
     // If shadowing is forbidden, see if the id corresponds to an underlying
     // native property.
     if (WrapperFactory::IsShadowingForbidden(wrapper)) {
-        JSObject *holder = Traits::getHolderObject(cx, wrapper);
+        JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper);
         js::PropertyDescriptor nativeProp;
         if (!Traits::resolveNativeProperty(cx, wrapper, holder, id, false, &nativeProp))
             return false;
         if (nativeProp.obj) {
             JS_ReportError(cx, "Permission denied to shadow native property");
             return false;
         }
     }
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -25,18 +25,16 @@ JSBool
 holder_set(JSContext *cx, JSHandleObject holder, JSHandleId id, JSBool strict, JSMutableHandleValue vp);
 
 namespace XrayUtils {
 
 extern JSClass HolderClass;
 
 bool CloneExpandoChain(JSContext *cx, JSObject *src, JSObject *dst);
 
-JSObject *createHolder(JSContext *cx, JSObject *wrapper);
-
 bool
 IsTransparent(JSContext *cx, JSObject *wrapper);
 
 JSObject *
 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper);
 
 }