Bug 887558 (part 4) - Introduce WrapperObject and CrossCrompartmentWrapperObject. r=jorendorff.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 27 Jun 2013 22:42:30 -0700
changeset 139359 bb2316d853d66f51808c8c506f974784e851fbe7
parent 139358 c525538afb69590f425ab6db1a8ad70cc88ccf04
child 139360 7ae54936c2a4398834d7f637775ecdfc616f7113
push id31351
push usernnethercote@mozilla.com
push dateSun, 21 Jul 2013 23:20:20 +0000
treeherdermozilla-inbound@bb2316d853d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs887558
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 887558 (part 4) - Introduce WrapperObject and CrossCrompartmentWrapperObject. r=jorendorff.
js/src/ion/AsmJS.cpp
js/src/jsapi.cpp
js/src/jsboolinlines.h
js/src/jsclone.cpp
js/src/jscompartment.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsmemorymetrics.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsproxy.cpp
js/src/jsweakmap.cpp
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/WrapperObject.h
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ion/AsmJS.h"
 
 #include "mozilla/Move.h"
 
 #include "jsmath.h"
 #include "jsworkers.h"
+#include "jswrapper.h"
 #include "prmjtime.h"
 
 #ifdef MOZ_VTUNE
 # include "jitprofiling.h"
 #endif
 
 #include "frontend/Parser.h"
 #include "ion/AsmJSModule.h"
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -68,16 +68,17 @@
 #include "vm/ErrorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WeakMapObject.h"
+#include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 #include "yarr/BumpPointerAllocator.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
@@ -1583,18 +1584,18 @@ JS_WrapId(JSContext *cx, jsid *idp)
  * we intentionally crash instead.
  */
 
 JS_PUBLIC_API(JSObject *)
 JS_TransplantObject(JSContext *cx, HandleObject origobj, HandleObject target)
 {
     AssertHeapIsIdle(cx);
     JS_ASSERT(origobj != target);
-    JS_ASSERT(!IsCrossCompartmentWrapper(origobj));
-    JS_ASSERT(!IsCrossCompartmentWrapper(target));
+    JS_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
+    JS_ASSERT(!target->is<CrossCompartmentWrapperObject>());
 
     AutoMaybeTouchDeadZones agc(cx);
     AutoDisableProxyCheck adpc(cx->runtime());
 
     JSCompartment *destination = target->compartment();
     RootedValue origv(cx, ObjectValue(*origobj));
     RootedObject newIdentity(cx);
 
@@ -1659,20 +1660,20 @@ js_TransplantObjectWithWrapper(JSContext
                                HandleObject origwrapper,
                                HandleObject targetobj,
                                HandleObject targetwrapper)
 {
     AutoMaybeTouchDeadZones agc(cx);
     AutoDisableProxyCheck adpc(cx->runtime());
 
     AssertHeapIsIdle(cx);
-    JS_ASSERT(!IsCrossCompartmentWrapper(origobj));
-    JS_ASSERT(!IsCrossCompartmentWrapper(origwrapper));
-    JS_ASSERT(!IsCrossCompartmentWrapper(targetobj));
-    JS_ASSERT(!IsCrossCompartmentWrapper(targetwrapper));
+    JS_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
+    JS_ASSERT(!origwrapper->is<CrossCompartmentWrapperObject>());
+    JS_ASSERT(!targetobj->is<CrossCompartmentWrapperObject>());
+    JS_ASSERT(!targetwrapper->is<CrossCompartmentWrapperObject>());
 
     RootedObject newWrapper(cx);
     JSCompartment *destination = targetobj->compartment();
 
     // |origv| is the map entry we're looking up. The map entries are going to
     // be for |origobj|, not |origwrapper|.
     Value origv = ObjectValue(*origobj);
 
@@ -2061,17 +2062,17 @@ JS_GetClassPrototype(JSContext *cx, JSPr
 }
 
 JS_PUBLIC_API(JSProtoKey)
 JS_IdentifyClassPrototype(JSContext *cx, JSObject *obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    JS_ASSERT(!IsCrossCompartmentWrapper(obj));
+    JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
     return js_IdentifyClassPrototype(obj);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetObjectPrototype(JSContext *cx, JSObject *forObj)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, forObj);
--- a/js/src/jsboolinlines.h
+++ b/js/src/jsboolinlines.h
@@ -5,17 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsboolinlines_h
 #define jsboolinlines_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Likely.h"
 
+#include "jswrapper.h"
+
 #include "js/RootingAPI.h"
+#include "vm/WrapperObject.h"
 
 #include "vm/BooleanObject-inl.h"
 
 namespace js {
 
 bool
 BooleanGetPrimitiveValueSlow(HandleObject, JSContext *);
 
@@ -26,17 +29,17 @@ BooleanGetPrimitiveValue(HandleObject ob
         return obj->as<BooleanObject>().unbox();
 
     return BooleanGetPrimitiveValueSlow(obj, cx);
 }
 
 inline bool
 EmulatesUndefined(JSObject *obj)
 {
-    JSObject *actual = MOZ_LIKELY(!obj->isWrapper()) ? obj : UncheckedUnwrap(obj);
+    JSObject *actual = MOZ_LIKELY(!obj->is<WrapperObject>()) ? obj : UncheckedUnwrap(obj);
     bool emulatesUndefined = actual->getClass()->emulatesUndefined();
     MOZ_ASSERT_IF(emulatesUndefined, obj->type()->flags & types::OBJECT_FLAG_EMULATES_UNDEFINED);
     return emulatesUndefined;
 }
 
 } /* namespace js */
 
 #endif /* jsboolinlines_h */
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -24,22 +24,24 @@
  *
  * So during writing, we add objects to the memory when first encountering
  * them. When reading a typed array, a placeholder is pushed onto allObjs until
  * the ArrayBuffer has been read, then it is updated with the actual typed
  * array object.
  */
 
 #include "jsclone.h"
+#include "jswrapper.h"
 
 #include "mozilla/FloatingPoint.h"
 
 #include "jsdate.h"
 
 #include "vm/TypedArrayObject.h"
+#include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::IsNaN;
 using mozilla::LittleEndian;
@@ -563,17 +565,17 @@ JS_PUBLIC_API(JSBool)
 JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v)
 {
     JS_ASSERT(v.isObject());
     assertSameCompartment(w->context(), v);
     RootedObject obj(w->context(), &v.toObject());
 
     // If the object is a security wrapper, see if we're allowed to unwrap it.
     // If we aren't, throw.
-    if (obj->isWrapper())
+    if (obj->is<WrapperObject>())
         obj = CheckedUnwrap(obj);
     if (!obj) {
         JS_ReportError(w->context(), "Permission denied to access object");
         return false;
     }
     return w->writeTypedArray(obj);
 }
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -17,16 +17,17 @@
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "ion/IonCompartment.h"
 #endif
 #include "js/RootingAPI.h"
 #include "vm/StopIterationObject.h"
+#include "vm/WrapperObject.h"
 
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 #include "gc/Barrier-inl.h"
 
 using namespace js;
@@ -267,17 +268,17 @@ JSCompartment::wrap(JSContext *cx, Mutab
 
     RootedValue key(cx, vp);
 
     /* If we already have a wrapper for this value, use it. */
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) {
         vp.set(p->value);
         if (vp.isObject()) {
             DebugOnly<JSObject *> obj = &vp.toObject();
-            JS_ASSERT(obj->isCrossCompartmentWrapper());
+            JS_ASSERT(obj->is<CrossCompartmentWrapperObject>());
             JS_ASSERT(obj->getParent() == global);
         }
         return true;
     }
 
     if (vp.isString()) {
         Rooted<JSLinearString *> str(cx, vp.toString()->ensureLinear(cx));
         if (!str)
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -17,16 +17,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsscript.h"
+#include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/StringBuffer.h"
 
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -14,16 +14,17 @@
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 #include "jsweakmap.h"
 #include "jswatchpoint.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
+#include "vm/WrapperObject.h"
 
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
 using mozilla::PodArrayZero;
@@ -67,17 +68,17 @@ JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSObject *objArg)
 {
     RootedObject obj(cx, objArg);
 
     /*
      * We unwrap wrappers here. This is a little weird, but it's what's being
      * asked of us.
      */
-    if (obj->isWrapper())
+    if (obj->is<WrapperObject>())
         obj = UncheckedUnwrap(obj);
 
     /*
      * Innerize the target_obj so that we compile in the correct (inner)
      * scope.
      */
     if (JSObjectOp op = obj->getClass()->ext.innerObject)
         obj = op(cx, obj);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -18,24 +18,26 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsobj.h"
 #include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
+#include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
+#include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsfuninlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/Stack-inl.h"
 
@@ -122,17 +124,17 @@ fun_getProperty(JSContext *cx, HandleObj
 
         if (!cx->compartment()->wrap(cx, vp))
             return false;
 
         /*
          * Censor the caller if we don't have full access to it.
          */
         RootedObject caller(cx, &vp.toObject());
-        if (caller->isWrapper() && !Wrapper::wrapperHandler(caller)->isSafeToUnwrap()) {
+        if (caller->is<WrapperObject>() && !Wrapper::wrapperHandler(caller)->isSafeToUnwrap()) {
             vp.setNull();
         } else if (caller->is<JSFunction>()) {
             JSFunction *callerFun = &caller->as<JSFunction>();
             if (callerFun->isInterpreted() && callerFun->strict()) {
                 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                              JSMSG_CALLER_IS_STRICT);
                 return false;
             }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -64,16 +64,17 @@ using mozilla::Swap;
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "vm/Debugger.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/ForkJoin.h"
+#include "vm/WrapperObject.h"
 #include "ion/IonCode.h"
 #ifdef JS_ION
 # include "ion/BaselineJIT.h"
 #endif
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
@@ -3379,17 +3380,17 @@ GetNextZoneGroup(JSRuntime *rt)
  * The list is traversed and then unlinked in
  * MarkIncomingCrossCompartmentPointers.
  */
 
 static bool
 IsGrayListObject(JSObject *obj)
 {
     JS_ASSERT(obj);
-    return IsCrossCompartmentWrapper(obj) && !IsDeadProxyObject(obj);
+    return obj->is<CrossCompartmentWrapperObject>() && !IsDeadProxyObject(obj);
 }
 
 /* static */ unsigned
 ProxyObject::grayLinkSlot(JSObject *obj)
 {
     JS_ASSERT(IsGrayListObject(obj));
     return ProxyObject::EXTRA_SLOT + 1;
 }
--- a/js/src/jsmemorymetrics.cpp
+++ b/js/src/jsmemorymetrics.cpp
@@ -13,16 +13,17 @@
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
 #include "ion/BaselineJIT.h"
 #include "ion/Ion.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
+#include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 
 using mozilla::DebugOnly;
 
 using namespace js;
 
 using JS::RuntimeStats;
@@ -172,17 +173,17 @@ StatsCellCallback(JSRuntime *rt, void *d
     switch (traceKind) {
       case JSTRACE_OBJECT: {
         JSObject *obj = static_cast<JSObject *>(thing);
         CompartmentStats *cStats = GetCompartmentStats(obj->compartment());
         if (obj->is<JSFunction>())
             cStats->gcHeapObjectsFunction += thingSize;
         else if (obj->is<ArrayObject>())
             cStats->gcHeapObjectsDenseArray += thingSize;
-        else if (obj->isCrossCompartmentWrapper())
+        else if (obj->is<CrossCompartmentWrapperObject>())
             cStats->gcHeapObjectsCrossCompartmentWrapper += thingSize;
         else
             cStats->gcHeapObjectsOrdinary += thingSize;
 
         JS::ObjectsExtraSizes objectsExtra;
         obj->sizeOfExcludingThis(rtStats->mallocSizeOf_, &objectsExtra);
         cStats->objectsExtra.add(objectsExtra);
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1822,17 +1822,17 @@ JS_CopyPropertiesFrom(JSContext *cx, JSO
 
 static bool
 CopySlots(JSContext *cx, HandleObject from, HandleObject to)
 {
     JS_ASSERT(!from->isNative() && !to->isNative());
     JS_ASSERT(from->getClass() == to->getClass());
 
     size_t n = 0;
-    if (from->isWrapper() &&
+    if (from->is<WrapperObject>() &&
         (Wrapper::wrapperHandler(from)->flags() &
          Wrapper::CROSS_COMPARTMENT)) {
         to->setSlot(0, from->getSlot(0));
         to->setSlot(1, from->getSlot(1));
         n = 2;
     }
 
     size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1006,20 +1006,16 @@ class JSObject : public js::ObjectImpl
     }
 
     template <class T>
     const T &as() const {
         JS_ASSERT(is<T>());
         return *static_cast<const T *>(this);
     }
 
-    /* Subtypes of Proxy. */
-    inline bool isWrapper()                 const;
-    inline bool isCrossCompartmentWrapper() const;
-
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
 
 #ifdef DEBUG
     void dump();
 #endif
 
   private:
     static void staticAsserts() {
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -4,18 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsobjinlines_h
 #define jsobjinlines_h
 
 #include "jsobj.h"
 
-#include "jswrapper.h"
-
 #include "vm/ArrayObject.h"
 #include "vm/DateObject.h"
 #include "vm/NumberObject.h"
 #include "vm/Probes.h"
 #include "vm/ScopeObject.h"
 #include "vm/StringObject.h"
 
 #include "jsatominlines.h"
@@ -757,28 +755,16 @@ JSObject::getElementAttributes(JSContext
                                uint32_t index, unsigned *attrsp)
 {
     JS::RootedId id(cx);
     if (!js::IndexToId(cx, index, &id))
         return false;
     return getGenericAttributes(cx, obj, id, attrsp);
 }
 
-inline bool
-JSObject::isCrossCompartmentWrapper() const
-{
-    return js::IsCrossCompartmentWrapper(const_cast<JSObject*>(this));
-}
-
-inline bool
-JSObject::isWrapper() const
-{
-    return js::IsWrapper(const_cast<JSObject*>(this));
-}
-
 inline js::GlobalObject &
 JSObject::global() const
 {
 #ifdef DEBUG
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
 #endif
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -8,18 +8,20 @@
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsprvtd.h"
+#include "jswrapper.h"
 
 #include "gc/Marking.h"
+#include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 using mozilla::ArrayLength;
@@ -2998,17 +3000,17 @@ proxy_DeleteSpecial(JSContext *cx, Handl
 }
 
 /* static */ void
 ProxyObject::trace(JSTracer *trc, JSObject *obj)
 {
     ProxyObject *proxy = &obj->as<ProxyObject>();
 
 #ifdef DEBUG
-    if (!trc->runtime->gcDisableStrictProxyCheckingCount && proxy->isWrapper()) {
+    if (!trc->runtime->gcDisableStrictProxyCheckingCount && proxy->is<WrapperObject>()) {
         JSObject *referent = &proxy->private_().toObject();
         if (referent->compartment() != proxy->compartment()) {
             /*
              * Assert that this proxy is tracked in the wrapper map. We maintain
              * the invariant that the wrapped object is the key in the wrapper map.
              */
             Value key = ObjectValue(*referent);
             WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
@@ -3021,17 +3023,17 @@ ProxyObject::trace(JSTracer *trc, JSObje
     // nuke() to cope.
     MarkCrossCompartmentSlot(trc, obj, proxy->slotOfPrivate(), "private");
     MarkSlot(trc, proxy->slotOfExtra(0), "extra0");
 
     /*
      * The GC can use the second reserved slot to link the cross compartment
      * wrappers into a linked list, in which case we don't want to trace it.
      */
-    if (!IsCrossCompartmentWrapper(proxy))
+    if (!proxy->is<CrossCompartmentWrapperObject>())
         MarkSlot(trc, proxy->slotOfExtra(1), "extra1");
 }
 
 static void
 proxy_TraceFunction(JSTracer *trc, JSObject *obj)
 {
     // NB: If you add new slots here, make sure to change
     // nuke() to cope.
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -7,16 +7,17 @@
 #include "jsweakmap.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
+#include "jswrapper.h"
 
 #include "vm/GlobalObject.h"
 #include "vm/WeakMapObject.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Barrier-inl.h"
 
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -9,33 +9,27 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsexn.h"
 #include "jsgc.h"
 #include "jsiter.h"
 
 #include "vm/ErrorObject.h"
+#include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 int js::sWrapperFamily;
 
-void *
-Wrapper::getWrapperFamily()
-{
-    return &sWrapperFamily;
-}
-
 JSObject *
-Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
-             Wrapper *handler)
+Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler)
 {
     JS_ASSERT(parent);
 
     AutoMarkInDeadZone amd(cx->zone());
 
     RootedValue priv(cx, ObjectValue(*obj));
     return NewProxyObject(cx, handler, priv, proto, parent,
                           obj->isCallable() ? ProxyIsCallable : ProxyNotCallable);
@@ -47,32 +41,32 @@ Wrapper::Renew(JSContext *cx, JSObject *
     JS_ASSERT(!obj->isCallable());
     existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
     return existing;
 }
 
 Wrapper *
 Wrapper::wrapperHandler(JSObject *wrapper)
 {
-    JS_ASSERT(wrapper->isWrapper());
+    JS_ASSERT(wrapper->is<WrapperObject>());
     return static_cast<Wrapper*>(wrapper->as<ProxyObject>().handler());
 }
 
 JSObject *
 Wrapper::wrappedObject(JSObject *wrapper)
 {
-    JS_ASSERT(wrapper->isWrapper());
+    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->isWrapper() &&
+    while (wrapped->is<WrapperObject>() &&
            !JS_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) {
         flags |= Wrapper::wrapperHandler(wrapped)->flags();
         wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
     }
     if (flagsp)
         *flagsp = flags;
     return wrapped;
 }
@@ -86,31 +80,31 @@ js::CheckedUnwrap(JSObject *obj, bool st
         if (!obj || obj == wrapper)
             return obj;
     }
 }
 
 JS_FRIEND_API(JSObject *)
 js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter)
 {
-    if (!obj->isWrapper() ||
+    if (!obj->is<WrapperObject>() ||
         JS_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter))
     {
         return obj;
     }
 
     Wrapper *handler = Wrapper::wrapperHandler(obj);
     return handler->isSafeToUnwrap() ? Wrapper::wrappedObject(obj) : NULL;
 }
 
 bool
-js::IsCrossCompartmentWrapper(JSObject *wrapper)
+js::IsCrossCompartmentWrapper(JSObject *obj)
 {
-    return wrapper->isWrapper() &&
-           !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
+    return IsWrapper(obj) &&
+           !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
 Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily)
                                                     , mFlags(flags)
                                                     , mSafeToUnwrap(true)
 {
     setHasPrototype(hasPrototype);
 }
@@ -125,17 +119,17 @@ Wrapper Wrapper::singletonWithPrototype(
 /* Compartments. */
 
 extern JSObject *
 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
                              HandleObject wrappedProto, HandleObject parent,
                              unsigned flags)
 {
     // Allow wrapping outer window proxies.
-    JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
+    JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
     return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton);
 }
 
 ErrorCopier::~ErrorCopier()
 {
     JSContext *cx = ac.ref().context()->asJSContext();
     if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) {
         RootedValue exc(cx, cx->getPendingException());
@@ -466,17 +460,17 @@ CrossCompartmentWrapper::construct(JSCon
 }
 
 bool
 CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                     CallArgs srcArgs)
 {
     RootedObject wrapper(cx, &srcArgs.thisv().toObject());
     JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
-              !UncheckedUnwrap(wrapper)->isCrossCompartmentWrapper());
+              !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
 
     RootedObject wrapped(cx, wrappedObject(wrapper));
     {
         AutoCompartment call(cx, wrapped);
         InvokeArgs dstArgs(cx);
         if (!dstArgs.init(srcArgs.length()))
             return false;
 
@@ -492,20 +486,20 @@ CrossCompartmentWrapper::nativeCall(JSCo
             *dst = source.get();
 
             // Handle |this| specially. When we rewrap on the other side of the
             // membrane, we might apply a same-compartment security wrapper that
             // will stymie this whole process. If that happens, unwrap the wrapper.
             // This logic can go away when same-compartment security wrappers go away.
             if ((src == srcArgs.base() + 1) && dst->isObject()) {
                 RootedObject thisObj(cx, &dst->toObject());
-                if (thisObj->isWrapper() &&
+                if (thisObj->is<WrapperObject>() &&
                     !Wrapper::wrapperHandler(thisObj)->isSafeToUnwrap())
                 {
-                    JS_ASSERT(!IsCrossCompartmentWrapper(thisObj));
+                    JS_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
                     *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
                 }
             }
         }
 
         if (!CallNonGenericMethod(cx, test, impl, dstArgs))
             return false;
 
@@ -837,17 +831,17 @@ js::IsDeadProxyObject(JSObject *obj)
 {
     return obj->is<ProxyObject>() &&
            obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
 }
 
 void
 js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper)
 {
-    JS_ASSERT(IsCrossCompartmentWrapper(wrapper));
+    JS_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
 
     NotifyGCNukeWrapper(wrapper);
 
     wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
 
     JS_ASSERT(IsDeadProxyObject(wrapper));
 }
 
@@ -904,18 +898,18 @@ js::NukeCrossCompartmentWrappers(JSConte
 // Given a cross-compartment wrapper |wobj|, update it to point to
 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
 // useful even if wrapper already points to newTarget.
 bool
 js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg)
 {
     RootedObject wobj(cx, wobjArg);
     RootedObject newTarget(cx, newTargetArg);
-    JS_ASSERT(IsCrossCompartmentWrapper(wobj));
-    JS_ASSERT(!IsCrossCompartmentWrapper(newTarget));
+    JS_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
+    JS_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
     JSObject *origTarget = Wrapper::wrappedObject(wobj);
     JS_ASSERT(origTarget);
     Value origv = ObjectValue(*origTarget);
     JSCompartment *wcompartment = wobj->compartment();
 
     AutoDisableProxyCheck adpc(cx->runtime());
 
     // If we're mapping to a different target (as opposed to just recomputing
@@ -955,17 +949,17 @@ js::RemapWrapper(JSContext *cx, JSObject
     }
 
     // Before swapping, this wrapper came out of wrap(), which enforces the
     // invariant that the wrapper in the map points directly to the key.
     JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
 
     // Update the entry in the compartment's wrapper map to point to the old
     // wrapper, which has now been updated (via reuse or swap).
-    JS_ASSERT(wobj->isWrapper());
+    JS_ASSERT(wobj->is<WrapperObject>());
     wcompartment->putWrapper(ObjectValue(*newTarget), ObjectValue(*wobj));
     return true;
 }
 
 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
 // |newTarget|. All wrappers are recomputed.
 JS_FRIEND_API(bool)
 js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg,
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -61,18 +61,16 @@ class JS_FRIEND_API(Wrapper) : public Di
     }
 
     explicit Wrapper(unsigned flags, bool hasPrototype = false);
 
     virtual ~Wrapper();
 
     static Wrapper singleton;
     static Wrapper singletonWithPrototype;
-
-    static void *getWrapperFamily();
 };
 
 /* Base class for all cross compartment wrapper handlers. */
 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
 {
   public:
     CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -39,16 +39,17 @@
 #include "jswrapper.h"
 #include "perf/jsperf.h"
 
 #include "builtin/TestingFunctions.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/Parser.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
+#include "vm/WrapperObject.h"
 
 #include "prmjtime.h"
 
 #include "shell/jsoptparse.h"
 #include "shell/jsheaptools.h"
 
 #include "jsinferinlines.h"
 #include "jsscriptinlines.h"
@@ -2319,17 +2320,17 @@ Clone(JSContext *cx, unsigned argc, jsva
         JS_ReportError(cx, "Invalid arguments to clone");
         return false;
     }
 
     {
         Maybe<JSAutoCompartment> ac;
         RootedObject obj(cx, JSVAL_IS_PRIMITIVE(args[0]) ? NULL : &args[0].toObject());
 
-        if (obj && IsCrossCompartmentWrapper(obj)) {
+        if (obj && obj->is<CrossCompartmentWrapperObject>()) {
             obj = UncheckedUnwrap(obj);
             ac.construct(cx, obj);
             args[0] = ObjectValue(*obj);
         }
         if (obj && obj->is<JSFunction>()) {
             funobj = obj;
         } else {
             JSFunction *fun = JS_ValueToFunction(cx, args[0]);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -12,16 +12,17 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "ion/BaselineJIT.h"
 #include "js/Vector.h"
+#include "vm/WrapperObject.h"
 
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
 #include "jsopcodeinlines.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
@@ -2058,17 +2059,17 @@ Debugger::construct(JSContext *cx, unsig
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Check that the arguments, if any, are cross-compartment wrappers. */
     for (unsigned i = 0; i < argc; i++) {
         const Value &arg = args[i];
         if (!arg.isObject())
             return ReportObjectRequired(cx);
         JSObject *argobj = &arg.toObject();
-        if (!IsCrossCompartmentWrapper(argobj)) {
+        if (!argobj->is<CrossCompartmentWrapperObject>()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CCW_REQUIRED, "Debugger");
             return false;
         }
     }
 
     /* Get Debugger.prototype. */
     RootedValue v(cx);
     RootedObject callee(cx, &args.callee());
@@ -5004,17 +5005,17 @@ DebuggerObject_makeDebuggeeValue(JSConte
     return true;
 }
 
 static bool
 RequireGlobalObject(JSContext *cx, HandleValue dbgobj, HandleObject obj)
 {
     if (!obj->is<GlobalObject>()) {
         /* Help the poor programmer by pointing out wrappers around globals. */
-        if (obj->isWrapper()) {
+        if (obj->is<WrapperObject>()) {
             JSObject *unwrapped = js::UncheckedUnwrap(obj);
             if (unwrapped->is<GlobalObject>()) {
                 js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY,
                                          JSDVG_SEARCH_STACK, dbgobj, NullPtr(),
                                          "a global object", NULL);
                 return false;
             }
         }
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -16,22 +16,24 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jscntxt.h"
 #include "jscpucfg.h"
 #include "jsversion.h"
 #include "jsgc.h"
 #include "jsnum.h"
 #include "jsobj.h"
+#include "jswrapper.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
+#include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/GlobalObject-inl.h"
 
 # ifdef XP_WIN
@@ -2850,17 +2852,17 @@ JSBool
 DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject bufobj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj))
         return false;
 
-    if (bufobj->isWrapper() && UncheckedUnwrap(bufobj)->is<ArrayBufferObject>()) {
+    if (bufobj->is<WrapperObject>() && UncheckedUnwrap(bufobj)->is<ArrayBufferObject>()) {
         Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());
         Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx));
         if (!proto)
             return false;
 
         InvokeArgs args2(cx);
         if (!args2.init(args.length() + 1))
             return false;
new file mode 100644
--- /dev/null
+++ b/js/src/vm/WrapperObject.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_WrapperObject_h
+#define vm_WrapperObject_h
+
+#include "jsobj.h"
+#include "jswrapper.h"
+
+#include "vm/ProxyObject.h"
+
+namespace js {
+
+// Proxy family for wrappers.
+extern int sWrapperFamily;
+
+class WrapperObject : public ProxyObject
+{
+};
+
+class CrossCompartmentWrapperObject : public WrapperObject
+{
+};
+
+} // namespace js
+
+template<>
+inline bool
+JSObject::is<js::WrapperObject>() const
+{
+    return IsWrapper(const_cast<JSObject*>(this));
+}
+
+template<>
+inline bool
+JSObject::is<js::CrossCompartmentWrapperObject>() const
+{
+    return IsCrossCompartmentWrapper(const_cast<JSObject*>(this));
+}
+
+#endif /* vm_WrapperObject_h */