Bug 836301 - Hoist some assertions, remove a bunch of no-op trap overrides, and add assertions that we've entered our policy. r=mrbkap
☠☠ backed out by dee88fe417fe ☠ ☠
authorBobby Holley <bobbyholley@gmail.com>
Fri, 22 Feb 2013 08:14:34 -0800
changeset 122669 4d301b2bcad047442fa34470893aff52b4df73ee
parent 122668 ab07392f24241194e485b42e9c5b598bba5aaf87
child 122670 8177cabc4e22a9f4f9bdbd241101e020b6bbd62c
push id24356
push usergszorc@mozilla.com
push dateSun, 24 Feb 2013 01:00:12 +0000
treeherdermozilla-central@195e706140d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs836301
milestone22.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 836301 - Hoist some assertions, remove a bunch of no-op trap overrides, and add assertions that we've entered our policy. r=mrbkap
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jsfriendapi.h
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/xpconnect/wrappers/ChromeObjectWrapper.cpp
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -876,16 +876,19 @@ JSRuntime::JSRuntime(JSUseHelperThreads 
     jitHardening(false),
     ionPcScriptCache(NULL),
     threadPool(this),
     ctypesActivityCallback(NULL),
     parallelWarmup(0),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     useHelperThreads_(useHelperThreads),
     requestedHelperThreadCount(-1),
+#ifdef DEBUG
+    enteredPolicy(NULL),
+#endif
     rngNonce(0)
 {
     /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
     JS_INIT_CLIST(&onNewGlobalObjectWatchers);
 
     PodZero(&debugHooks);
     PodZero(&atomState);
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -564,17 +564,17 @@ struct MallocProvider
     }
 
     JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
 };
 
 namespace gc {
 class MarkingValidator;
 } // namespace gc
-
+class AutoEnterPolicy;
 } // namespace js
 
 struct JSRuntime : js::RuntimeFriendFields,
                    public js::MallocProvider<JSRuntime>
 {
     /*
      * Per-thread data for the main thread that is associated with
      * this JSRuntime, as opposed to any worker threads used in
@@ -1296,17 +1296,21 @@ struct JSRuntime : js::RuntimeFriendFiel
 #ifdef JS_THREADSAFE
         if (requestedHelperThreadCount < 0)
             return js::GetCPUCount() - 1;
         return requestedHelperThreadCount;
 #else
         return 0;
 #endif
     }
+#ifdef DEBUG
+  public:
+    js::AutoEnterPolicy *enteredPolicy;
 
+#endif
   private:
     /*
      * Used to ensure that compartments created at the same time get different
      * random number sequences. See js::InitRandom.
      */
     uint64_t rngNonce;
 
   public:
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1411,11 +1411,18 @@ class JS_FRIEND_API(AutoCTypesActivityCa
     void DoEndCallback() {
         if (callback) {
             callback(cx, endType);
             callback = NULL;
         }
     }
 };
 
+#ifdef DEBUG
+extern JS_FRIEND_API(void)
+assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id);
+#else
+inline void assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id) {};
+#endif
+
 } /* namespace js */
 
 #endif /* jsfriendapi_h___ */
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -58,16 +58,48 @@ js::AutoEnterPolicy::reportError(JSConte
     } else {
         JSString *str = IdToString(cx, id);
         const jschar *prop = str ? str->getCharsZ(cx) : NULL;
         JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
                                JSMSG_PROPERTY_ACCESS_DENIED, prop);
     }
 }
 
+#ifdef DEBUG
+JS_FRIEND_API(void)
+js::AutoEnterPolicy::recordEnter(JSContext *cx, JSObject *proxy, jsid id)
+{
+    if (allowed()) {
+        context = cx;
+        enteredProxy.construct(cx, proxy);
+        enteredId.construct(cx, id);
+        prev = cx->runtime->enteredPolicy;
+        cx->runtime->enteredPolicy = this;
+    }
+}
+
+JS_FRIEND_API(void)
+js::AutoEnterPolicy::recordLeave()
+{
+    if (!enteredProxy.empty()) {
+        JS_ASSERT(context->runtime->enteredPolicy == this);
+        context->runtime->enteredPolicy = prev;
+    }
+}
+
+JS_FRIEND_API(void)
+js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id)
+{
+    MOZ_ASSERT(proxy->isProxy());
+    MOZ_ASSERT(cx->runtime->enteredPolicy);
+    MOZ_ASSERT(cx->runtime->enteredPolicy->enteredProxy.ref().get() == proxy);
+    MOZ_ASSERT(cx->runtime->enteredPolicy->enteredId.ref().get() == id);
+}
+#endif
+
 BaseProxyHandler::BaseProxyHandler(void *family)
   : mFamily(family),
     mHasPrototype(false),
     mHasPolicy(false)
 {
 }
 
 BaseProxyHandler::~BaseProxyHandler()
@@ -80,40 +112,43 @@ BaseProxyHandler::enter(JSContext *cx, J
 {
     *bp = true;
     return true;
 }
 
 bool
 BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp)
 {
+    assertEnteredPolicy(cx, proxy_, id_);
     RootedObject proxy(cx, proxy_);
     RootedId id(cx, id_);
     AutoPropertyDescriptorRooter desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, &desc, 0))
         return false;
     *bp = !!desc.obj;
     return true;
 }
 
 bool
 BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp)
 {
+    assertEnteredPolicy(cx, proxy_, id_);
     RootedObject proxy(cx, proxy_);
     RootedId id(cx, id_);
     AutoPropertyDescriptorRooter desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0))
         return false;
     *bp = !!desc.obj;
     return true;
 }
 
 bool
 BaseProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, Value *vp)
 {
+    assertEnteredPolicy(cx, proxy, id_);
     RootedObject receiver(cx, receiver_);
     RootedId id(cx, id_);
 
     AutoPropertyDescriptorRooter desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, &desc, 0))
         return false;
     if (!desc.obj) {
         vp->setUndefined();
@@ -145,32 +180,34 @@ bool
 BaseProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject *receiver_, uint32_t index, Value *vp, bool *present)
 {
     RootedObject proxy(cx, proxy_);
     RootedObject receiver(cx, receiver_);
 
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
+    assertEnteredPolicy(cx, proxy, id);
 
     if (!has(cx, proxy, id, present))
         return false;
 
     if (!*present) {
         Debug_SetValueRangeToCrashOnTouch(vp, 1);
         return true;
     }
 
     return get(cx, proxy, receiver, id, vp);
 }
 
 bool
 BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, bool strict,
                       Value *vp)
 {
+    assertEnteredPolicy(cx, proxy_, id_);
     RootedObject proxy(cx, proxy_), receiver(cx, receiver_);
     RootedId id(cx, id_);
 
     AutoPropertyDescriptorRooter desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, JSRESOLVE_ASSIGNING))
         return false;
     /* The control-flow here differs from ::get() because of the fall-through case below. */
     if (desc.obj) {
@@ -237,44 +274,47 @@ BaseProxyHandler::set(JSContext *cx, JSO
     desc.getter = NULL;
     desc.setter = NULL; // Pick up the class getter/setter.
     return defineProperty(cx, receiver, id, &desc);
 }
 
 bool
 BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, proxyArg, JSID_VOID);
     JS_ASSERT(props.length() == 0);
 
     RootedObject proxy(cx, proxyArg);
 
     if (!getOwnPropertyNames(cx, proxy, props))
         return false;
 
     /* Select only the enumerable properties through in-place iteration. */
     AutoPropertyDescriptorRooter desc(cx);
     size_t i = 0;
     for (size_t j = 0, len = props.length(); j < len; j++) {
         JS_ASSERT(i <= j);
         jsid id = props[j];
+        AutoWaivePolicy policy(cx, proxy, id);
         if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0))
             return false;
         if (desc.obj && (desc.attrs & JSPROP_ENUMERATE))
             props[i++] = id;
     }
 
     JS_ASSERT(i <= props.length());
     props.resize(i);
 
     return true;
 }
 
 bool
 BaseProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp)
 {
+    assertEnteredPolicy(cx, proxy_, JSID_VOID);
     RootedObject proxy(cx, proxy_);
 
     AutoIdVector props(cx);
     if ((flags & JSITER_OWNONLY)
         ? !keys(cx, proxy, props)
         : !enumerate(cx, proxy, props)) {
         return false;
     }
@@ -286,28 +326,30 @@ BaseProxyHandler::iterate(JSContext *cx,
     *vp = value;
     return true;
 }
 
 bool
 BaseProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc,
                        Value *vp)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     AutoValueRooter rval(cx);
     RootedValue call(cx, GetCall(proxy));
     JSBool ok = Invoke(cx, vp[1], call, argc, JS_ARGV(cx, vp), rval.addr());
     if (ok)
         JS_SET_RVAL(cx, vp, rval.value());
     return ok;
 }
 
 bool
 BaseProxyHandler::construct(JSContext *cx, JSObject *proxy_, unsigned argc,
                             Value *argv, Value *rval)
 {
+    assertEnteredPolicy(cx, proxy_, JSID_VOID);
     RootedObject proxy(cx, proxy_);
     RootedValue fval(cx, GetConstruct(proxy_));
     if (fval.isUndefined())
         fval = GetCall(proxy);
     return InvokeConstructor(cx, fval, argc, argv, rval);
 }
 
 JSString *
@@ -316,16 +358,17 @@ BaseProxyHandler::obj_toString(JSContext
     return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy)
                                  ? "[object Function]"
                                  : "[object Object]");
 }
 
 JSString *
 BaseProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     Value fval = GetCall(proxy);
     if (IsFunctionProxy(proxy) &&
         (fval.isPrimitive() || !fval.toObject().isFunction())) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_INCOMPATIBLE_PROTO,
                              js_Function_str, js_toString_str,
                              "object");
         return NULL;
@@ -361,16 +404,17 @@ BaseProxyHandler::nativeCall(JSContext *
 {
     ReportIncompatible(cx, args);
     return false;
 }
 
 bool
 BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     RootedValue val(cx, ObjectValue(*proxy.get()));
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, val, NullPtr());
     return false;
 }
 
 bool
 BaseProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
@@ -397,16 +441,18 @@ BaseProxyHandler::getPrototypeOf(JSConte
     return true;
 }
 
 
 bool
 DirectProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy,
                                           jsid id, PropertyDescriptor *desc, unsigned flags)
 {
+    assertEnteredPolicy(cx, proxy, id);
+    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
     RootedObject target(cx, GetProxyTargetObject(proxy));
     return JS_GetPropertyDescriptorById(cx, target, id, 0, desc);
 }
 
 static bool
 GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, jsid id, unsigned flags,
                          JSPropertyDescriptor *desc)
 {
@@ -421,57 +467,63 @@ GetOwnPropertyDescriptor(JSContext *cx, 
         desc->obj = NULL;
     return true;
 }
 
 bool
 DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
                                              jsid id, PropertyDescriptor *desc, unsigned flags)
 {
+    assertEnteredPolicy(cx, proxy, id);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     return GetOwnPropertyDescriptor(cx, target, id, 0, desc);
 }
 
 bool
 DirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_,
                                    PropertyDescriptor *desc)
 {
+    assertEnteredPolicy(cx, proxy, id_);
     RootedObject obj(cx, GetProxyTargetObject(proxy));
     Rooted<jsid> id(cx, id_);
     Rooted<Value> v(cx, desc->value);
     return CheckDefineProperty(cx, obj, id, v, desc->getter, desc->setter, desc->attrs) &&
            JS_DefinePropertyById(cx, obj, id, v, desc->getter, desc->setter, desc->attrs);
 }
 
 bool
 DirectProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy,
                                         AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props);
 }
 
 bool
 DirectProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     RootedValue v(cx);
+    assertEnteredPolicy(cx, proxy, id);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     if (!JS_DeletePropertyById2(cx, target, id, v.address()))
         return false;
     JSBool b;
     if (!JS_ValueToBoolean(cx, v, &b))
         return false;
     *bp = !!b;
     return true;
 }
 
 bool
 DirectProxyHandler::enumerate(JSContext *cx, JSObject *proxy,
                               AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
+    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
     RootedObject target(cx, GetProxyTargetObject(proxy));
     return GetPropertyNames(cx, target, 0, &props);
 }
 
 bool
 DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                CallArgs args)
 {
@@ -483,16 +535,17 @@ DirectProxyHandler::nativeCall(JSContext
 
     return CallNativeImpl(cx, impl, args);
 }
 
 bool
 DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
                                 bool *bp)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     JSBool b;
     RootedObject target(cx, GetProxyTargetObject(proxy));
     if (!JS_HasInstance(cx, target, v, &b))
         return false;
     *bp = !!b;
     return true;
 }
 
@@ -503,23 +556,25 @@ DirectProxyHandler::objectClassIs(JSObje
 {
     RootedObject obj(cx, GetProxyTargetObject(proxy));
     return ObjectClassIs(obj, classValue, cx);
 }
 
 JSString *
 DirectProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     return obj_toStringHelper(cx, GetProxyTargetObject(proxy));
 }
 
 JSString *
 DirectProxyHandler::fun_toString(JSContext *cx, JSObject *proxy,
                                  unsigned indent)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     return fun_toStringHelper(cx, target, indent);
 }
 
 bool
 DirectProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy,
                                     RegExpGuard *g)
 {
@@ -545,75 +600,83 @@ DirectProxyHandler::weakmapKeyDelegate(J
 DirectProxyHandler::DirectProxyHandler(void *family)
   : BaseProxyHandler(family)
 {
 }
 
 bool
 DirectProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
+    assertEnteredPolicy(cx, proxy, id);
+    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
     JSBool found;
     RootedObject target(cx, GetProxyTargetObject(proxy));
     if (!JS_HasPropertyById(cx, target, id, &found))
         return false;
     *bp = !!found;
     return true;
 }
 
 bool
 DirectProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
+    assertEnteredPolicy(cx, proxy, id);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!JS_GetPropertyDescriptorById(cx, target, id, 0, &desc))
         return false;
     *bp = (desc.obj == target);
     return true;
 }
 
 bool
 DirectProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_,
                         jsid id_, Value *vp)
 {
+    assertEnteredPolicy(cx, proxy, id_);
     RootedObject receiver(cx, receiver_);
     RootedId id(cx, id_);
     RootedValue value(cx);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     if (!JSObject::getGeneric(cx, target, receiver, id, &value))
         return false;
 
     *vp = value;
     return true;
 }
 
 bool
 DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg,
                         jsid id_, bool strict, Value *vp)
 {
+    assertEnteredPolicy(cx, proxy, id_);
     RootedId id(cx, id_);
     Rooted<JSObject*> receiver(cx, receiverArg);
     RootedValue value(cx, *vp);
     RootedObject target(cx, GetProxyTargetObject(proxy));
     if (!JSObject::setGeneric(cx, target, receiver, id, &value, strict))
         return false;
 
     *vp = value;
     return true;
 }
 
 bool
 DirectProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
     return GetPropertyNames(cx, GetProxyTargetObject(proxy), JSITER_OWNONLY, &props);
 }
 
 bool
 DirectProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags,
                             Value *vp)
 {
+    assertEnteredPolicy(cx, proxy, JSID_VOID);
+    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
     Rooted<JSObject*> target(cx, GetProxyTargetObject(proxy));
     RootedValue value(cx);
     if (!GetIterator(cx, target, flags, &value))
         return false;
 
     *vp = value;
     return true;
 }
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -343,30 +343,74 @@ NewProxyObject(JSContext *cx, BaseProxyH
 JSObject *
 RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv);
 
 class AutoEnterPolicy {
   public:
     typedef BaseProxyHandler::Action Action;
     AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler,
                     JSObject *wrapper, jsid id, Action act, bool mayThrow)
+#ifdef DEBUG
+        : context(NULL)
+#endif
     {
         allow = handler->hasPolicy() ? handler->enter(cx, wrapper, id, act, &rv)
                                      : true;
+        recordEnter(cx, wrapper, id);
         if (!allow && !rv && mayThrow)
             reportError(cx, id);
     }
 
+    virtual ~AutoEnterPolicy() { recordLeave(); }
     inline bool allowed() { return allow; }
     inline bool returnValue() { JS_ASSERT(!allowed()); return rv; }
 
   protected:
+    // no-op constructor for subclass
+    AutoEnterPolicy()
+#ifdef DEBUG
+        : context(NULL)
+#endif
+        {};
     void reportError(JSContext *cx, jsid id);
     bool allow;
     bool rv;
+
+#ifdef DEBUG
+    JSContext *context;
+    mozilla::Maybe<RootedObject> enteredProxy;
+    mozilla::Maybe<RootedId> enteredId;
+    // NB: We explicitly don't track the entered action here, because sometimes
+    // SET traps do an implicit GET during their implementation, leading to
+    // spurious assertions.
+    AutoEnterPolicy *prev;
+    void recordEnter(JSContext *cx, JSObject *proxy, jsid id);
+    void recordLeave();
+
+    friend void assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id);
+#else
+    inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id) {}
+    inline void recordLeave() {}
+#endif
+
 };
 
+#ifdef DEBUG
+class AutoWaivePolicy : public AutoEnterPolicy {
+public:
+    AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id)
+    {
+        allow = true;
+        recordEnter(cx, proxy, id);
+    }
+};
+#else
+class AutoWaivePolicy {
+    public: AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) {};
+};
+#endif
+
 } /* namespace js */
 
 extern JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JSHandleObject obj);
 
 #endif
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -121,64 +121,16 @@ Wrapper::Wrapper(unsigned flags, bool ha
 {
     setHasPrototype(hasPrototype);
 }
 
 Wrapper::~Wrapper()
 {
 }
 
-bool
-Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapperArg,
-                               jsid id, PropertyDescriptor *desc, unsigned flags)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype.
-    return DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags);
-}
-
-bool
-Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapperArg,
-                                  jsid id, PropertyDescriptor *desc, unsigned flags)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags);
-}
-
-bool
-Wrapper::defineProperty(JSContext *cx, JSObject *wrapperArg, jsid id,
-                        PropertyDescriptor *desc)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::defineProperty(cx, wrapper, id, desc);
-}
-
-bool
-Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapperArg,
-                             AutoIdVector &props)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::getOwnPropertyNames(cx, wrapper, props);
-}
-
-bool
-Wrapper::delete_(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::delete_(cx, wrapper, id, bp);
-}
-
-bool
-Wrapper::enumerate(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype.
-    return DirectProxyHandler::enumerate(cx, wrapper, props);
-}
-
 /*
  * Ordinarily, the convert trap would require unwrapping. However, the default
  * implementation of convert, JS_ConvertStub, obtains a default value by calling
  * the toString/valueOf method on the wrapper, if any. Throwing if we can't unwrap
  * in this case would be overly conservative. To make matters worse, XPConnect sometimes
  * installs a custom convert trap that obtains a default value by calling the
  * toString method on the wrapper. Doing a puncture in this case would be overly
  * conservative as well. We deal with these anomalies by falling back to the DefaultValue
@@ -203,105 +155,16 @@ Wrapper::defaultValue(JSContext *cx, JSO
      * necessary because the DefaultValue algorithm above operates on the
      * wrapper, not the wrappee, so we want to delay the decision to switch
      * compartments until this point.
      */
     AutoCompartment call(cx, wrappedObject(wrapper));
     return DirectProxyHandler::defaultValue(cx, wrapper, hint, vp);
 }
 
-bool
-Wrapper::has(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype.
-    return DirectProxyHandler::has(cx, wrapper, id, bp);
-}
-
-bool
-Wrapper::hasOwn(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::hasOwn(cx, wrapper, id, bp);
-}
-
-bool
-Wrapper::get(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, Value *vp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::get(cx, wrapper, receiver, id, vp);
-}
-
-bool
-Wrapper::set(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, bool strict,
-             Value *vp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::set(cx, wrapper, receiver, id, strict, vp);
-}
-
-bool
-Wrapper::keys(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::keys(cx, wrapper, props);
-}
-
-bool
-Wrapper::iterate(JSContext *cx, JSObject *wrapperArg, unsigned flags, Value *vp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype.
-    return DirectProxyHandler::iterate(cx, wrapper, flags, vp);
-}
-
-bool
-Wrapper::call(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *vp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::call(cx, wrapper, argc, vp);
-}
-
-bool
-Wrapper::construct(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *argv, Value *vp)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    return DirectProxyHandler::construct(cx, wrapper, argc, argv, vp);
-}
-
-bool
-Wrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
-{
-    RootedObject wrapper(cx, &args.thisv().toObject());
-    // Note - we don't enter a policy here because our security architecture guards
-    // against nativeCall by overriding the trap itself in the right circumstances.
-    return DirectProxyHandler::nativeCall(cx, test, impl, args);
-}
-
-bool
-Wrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp)
-{
-    return DirectProxyHandler::hasInstance(cx, wrapper, v, bp);
-}
-
-JSString *
-Wrapper::obj_toString(JSContext *cx, JSObject *wrapperArg)
-{
-    RootedObject wrapper(cx, wrapperArg);
-    JSString *str = DirectProxyHandler::obj_toString(cx, wrapper);
-    return str;
-}
-
-JSString *
-Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent)
-{
-    JSString *str = DirectProxyHandler::fun_toString(cx, wrapper, indent);
-    return str;
-}
-
 Wrapper Wrapper::singleton((unsigned)0);
 Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
 
 /* Compartments. */
 
 extern JSObject *
 js::TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *objArg,
                              JSObject *wrappedProtoArg, JSObject *parentArg,
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -61,48 +61,16 @@ class JS_FRIEND_API(Wrapper) : public Di
         return mFlags;
     }
 
     explicit Wrapper(unsigned flags, bool hasPrototype = false);
 
     virtual ~Wrapper();
 
     /* ES5 Harmony fundamental wrapper traps. */
-    virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
-                                       jsid id, PropertyDescriptor *desc,
-                                       unsigned flags) MOZ_OVERRIDE;
-    virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper,
-                                          jsid id, PropertyDescriptor *desc,
-                                          unsigned flags) MOZ_OVERRIDE;
-    virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
-                                PropertyDescriptor *desc) MOZ_OVERRIDE;
-    virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
-                                     AutoIdVector &props) MOZ_OVERRIDE;
-    virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id,
-                         bool *bp) MOZ_OVERRIDE;
-    virtual bool enumerate(JSContext *cx, JSObject *wrapper,
-                           AutoIdVector &props) MOZ_OVERRIDE;
-
-    /* ES5 Harmony derived wrapper traps. */
-    virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE;
-    virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) MOZ_OVERRIDE;
-    virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict,
-                     Value *vp) MOZ_OVERRIDE;
-    virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE;
-    virtual bool iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
-    virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE;
-    virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE;
-    virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
-                            CallArgs args) MOZ_OVERRIDE;
-    virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) MOZ_OVERRIDE;
-    virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
-    virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint,
                               Value *vp) MOZ_OVERRIDE;
 
     static Wrapper singleton;
     static Wrapper singletonWithPrototype;
 
     static void *getWrapperFamily();
 };
--- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
+++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
@@ -9,16 +9,18 @@ namespace xpc {
 // from the wrappee's scope.
 //
 // One of the reasons for doing this is to allow standard operations like
 // chromeArray.forEach(..) to Just Work without explicitly listing them in
 // __exposedProps__. Since proxies don't automatically inherit behavior from
 // their prototype, we have to instrument the traps to do this manually.
 ChromeObjectWrapper ChromeObjectWrapper::singleton;
 
+using js::assertEnteredPolicy;
+
 static bool
 AllowedByBase(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act)
 {
     MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
                &ChromeObjectWrapper::singleton);
     bool bp;
     ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
     return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp);
@@ -53,16 +55,17 @@ PropIsFromStandardPrototype(JSContext *c
     return PropIsFromStandardPrototype(cx, &desc);
 }
 
 bool
 ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper,
                                            jsid id, js::PropertyDescriptor *desc,
                                            unsigned flags)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     // First, try a lookup on the base wrapper if permitted.
     desc->obj = NULL;
     if (AllowedByBase(cx, wrapper, id, Wrapper::GET) &&
         !ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id,
                                                         desc, flags)) {
         return false;
     }
 
@@ -82,16 +85,17 @@ ChromeObjectWrapper::getPropertyDescript
     // If not, try doing the lookup on the prototype.
     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
     return JS_GetPropertyDescriptorById(cx, wrapperProto, id, 0, desc);
 }
 
 bool
 ChromeObjectWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     // Try the lookup on the base wrapper if permitted.
     if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
         !ChromeObjectWrapperBase::has(cx, wrapper, id, bp))
     {
         return false;
     }
 
     // If we found something or have no prototype, we're done.
@@ -109,16 +113,17 @@ ChromeObjectWrapper::has(JSContext *cx, 
     *bp = !!desc.obj;
     return true;
 }
 
 bool
 ChromeObjectWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver,
                          jsid id, js::Value *vp)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     vp->setUndefined();
     JSPropertyDescriptor desc;
     // Only call through to the get trap on the underlying object if we're
     // allowed to see the property, and if what we'll find is not on a standard
     // prototype.
     if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
         !PropIsFromStandardPrototype(cx, wrapper, id))
     {
@@ -150,12 +155,18 @@ bool
 ChromeObjectWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id,
                            js::Wrapper::Action act, bool *bp)
 {
     if (AllowedByBase(cx, wrapper, id, act))
         return true;
     // COWs fail silently for GETs, and that also happens to be the only case
     // where we might want to redirect the lookup to the home prototype chain.
     *bp = (act == Wrapper::GET);
-    return *bp && PropIsFromStandardPrototype(cx, wrapper, id);
+    if (!*bp || id == JSID_VOID)
+        return false;
+
+    // Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor
+    // before we've fully entered the policy. Waive our policy.
+    js::AutoWaivePolicy policy(cx, wrapper, id);
+    return PropIsFromStandardPrototype(cx, wrapper, id);
 }
 
 }
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -58,60 +58,66 @@ FilterSetter(JSContext *cx, JSObject *wr
     return true;
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                       js::PropertyDescriptor *desc, unsigned flags)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     if (!Base::getPropertyDescriptor(cx, wrapper, id, desc, flags))
         return false;
     return FilterSetter<Policy>(cx, wrapper, id, desc);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                          js::PropertyDescriptor *desc,
                                                          unsigned flags)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags))
         return false;
     return FilterSetter<Policy>(cx, wrapper, id, desc);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return Base::getOwnPropertyNames(cx, wrapper, props) &&
            Filter<Policy>(cx, wrapper, props);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return Base::enumerate(cx, wrapper, props) &&
            Filter<Policy>(cx, wrapper, props);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return Base::keys(cx, wrapper, props) &&
            Filter<Policy>(cx, wrapper, props);
 }
 
 template <typename Base, typename Policy>
 bool
 FilteringWrapper<Base, Policy>::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     // We refuse to trigger the iterator hook across chrome wrappers because
     // we don't know how to censor custom iterator objects. Instead we trigger
     // the default proxy iterate trap, which will ask enumerate() for the list
     // of (censored) ids.
     return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp);
 }
 
 template <typename Base, typename Policy>
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1405,16 +1405,17 @@ DEBUG_CheckXBLLookup(JSContext *cx, JSPr
 #define DEBUG_CheckXBLLookup(a, b) {}
 #endif
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                  js::PropertyDescriptor *desc, unsigned flags)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper);
     if (Traits::isResolving(cx, holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     typename Traits::ResolvingIdImpl resolving(wrapper, id);
 
@@ -1523,16 +1524,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                                     PropertyDescriptor *desc, unsigned flags)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper);
     if (Traits::isResolving(cx, holder, id)) {
         desc->obj = NULL;
         return true;
     }
 
     typename Traits::ResolvingIdImpl resolving(wrapper, id);
 
@@ -1569,16 +1571,17 @@ XrayWrapper<Base, Traits>::getOwnPropert
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                           js::PropertyDescriptor *desc)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper, id)) {
         JSObject *obj = Traits::getTargetObject(wrapper);
         JSAutoCompartment ac(cx, obj);
         if (!JS_WrapPropertyDescriptor(cx, desc))
             return false;
 
         return JS_DefinePropertyById(cx, obj, id, desc->value, desc->getter, desc->setter,
@@ -1621,23 +1624,25 @@ XrayWrapper<Base, Traits>::definePropert
                                  wrappedDesc.attrs);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
                                                JS::AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
 {
+    assertEnteredPolicy(cx, wrapper, id);
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper, id)) {
         JSObject *obj = Traits::getTargetObject(wrapper);
 
         JSAutoCompartment ac(cx, obj);
 
         JSBool b;
         jsval v;
@@ -1664,16 +1669,17 @@ XrayWrapper<Base, Traits>::delete_(JSCon
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::enumerate(JSContext *cx, JSObject *wrapper, unsigned flags,
                                      JS::AutoIdVector &props)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper, JSID_VOID)) {
         JSObject *obj = Traits::getTargetObject(wrapper);
         JSAutoCompartment ac(cx, obj);
         return js::GetPropertyNames(cx, obj, flags, &props);
     }
 
     if (!AccessCheck::wrapperSubsumes(wrapper)) {
@@ -1757,24 +1763,26 @@ XrayWrapper<Base, Traits>::iterate(JSCon
     // Skip our Base if it isn't already ProxyHandler.
     return BaseProxyHandler::iterate(cx, wrapper, flags, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return Traits::call(cx, wrapper, argc, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::construct(JSContext *cx, JSObject *wrapper, unsigned argc,
                                      js::Value *argv, js::Value *rval)
 {
+    assertEnteredPolicy(cx, wrapper, JSID_VOID);
     return Traits::construct(cx, wrapper, argc, argv, rval);
 }
 
 /*
  * The Permissive / Security variants should be used depending on whether the
  * compartment of the wrapper is guranteed to subsume the compartment of the
  * wrapped object (i.e. - whether it is safe from a security perspective to
  * unwrap the wrapper).