Bug 1354733: Part 1 - Allow creating DeadObjectProxies directly. r=till
MozReview-Commit-ID: HnFgiRcA7l5
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -592,16 +592,22 @@ JS_PCToLineNumber(JSScript* script, jsby
}
JS_FRIEND_API(bool)
JS_IsDeadWrapper(JSObject* obj)
{
return IsDeadProxyObject(obj);
}
+JS_FRIEND_API(JSObject*)
+JS_NewDeadWrapper(JSContext* cx, JSObject* origObj)
+{
+ return NewDeadProxyObject(cx, origObj);
+}
+
void
js::TraceWeakMaps(WeakMapTracer* trc)
{
WeakMapBase::traceAllMappings(trc);
WatchpointMap::traceAll(trc);
}
extern JS_FRIEND_API(bool)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -91,16 +91,26 @@ JS_PCToLineNumber(JSScript* script, jsby
* Determine whether the given object is backed by a DeadObjectProxy.
*
* Such objects hold no other objects (they have no outgoing reference edges)
* and will throw if you touch them (e.g. by reading/writing a property).
*/
extern JS_FRIEND_API(bool)
JS_IsDeadWrapper(JSObject* obj);
+/**
+ * Creates a new dead wrapper object in the given scope. To be used when
+ * attempting to wrap objects from scopes which are already dead.
+ *
+ * If origObject is passed, it must be an proxy object, and will be
+ * used to determine the characteristics of the new dead wrapper.
+ */
+extern JS_FRIEND_API(JSObject*)
+JS_NewDeadWrapper(JSContext* cx, JSObject* origObject = nullptr);
+
/*
* Used by the cycle collector to trace through a shape or object group and
* all cycle-participating data it reaches, using bounded stack space.
*/
extern JS_FRIEND_API(void)
JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape);
extern JS_FRIEND_API(void)
JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group);
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -180,8 +180,42 @@ const char DeadObjectProxy<DeadProxyIsCa
bool
js::IsDeadProxyObject(JSObject* obj)
{
return IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton()) ||
IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyIsCallableIsConstructor>::singleton()) ||
IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton()) ||
IsDerivedProxyObject(obj, DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton());
}
+
+
+const BaseProxyHandler*
+js::SelectDeadProxyHandler(ProxyObject* obj)
+{
+ // When nuking scripted proxies, isCallable and isConstructor values for
+ // the proxy needs to be preserved.
+ uint32_t callable = obj->handler()->isCallable(obj);
+ uint32_t constructor = obj->handler()->isConstructor(obj);
+
+ if (callable) {
+ if (constructor)
+ return DeadObjectProxy<DeadProxyIsCallableIsConstructor>::singleton();
+ return DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton();
+ }
+
+ if (constructor)
+ return DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton();
+ return DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton();
+}
+
+JSObject*
+js::NewDeadProxyObject(JSContext* cx, JSObject* origObj)
+{
+ MOZ_ASSERT_IF(origObj, origObj->is<ProxyObject>());
+
+ const BaseProxyHandler* handler;
+ if (origObj && origObj->is<ProxyObject>())
+ handler = SelectDeadProxyHandler(&origObj->as<ProxyObject>());
+ else
+ handler = DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton();
+
+ return NewProxyObject(cx, handler, NullHandleValue, nullptr, ProxyOptions());
+}
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -6,16 +6,18 @@
#ifndef proxy_DeadObjectProxy_h
#define proxy_DeadObjectProxy_h
#include "js/Proxy.h"
namespace js {
+class ProxyObject;
+
enum DeadProxyIsCallableIsConstructorOption
{
DeadProxyNotCallableNotConstructor,
DeadProxyNotCallableIsConstructor,
DeadProxyIsCallableNotConstructor,
DeadProxyIsCallableIsConstructor
};
@@ -74,11 +76,17 @@ class DeadObjectProxy : public BaseProxy
}
static const char family;
};
bool
IsDeadProxyObject(JSObject* obj);
+const BaseProxyHandler*
+SelectDeadProxyHandler(ProxyObject* obj);
+
+JSObject*
+NewDeadProxyObject(JSContext* cx, JSObject* origObj = nullptr);
+
} /* namespace js */
#endif /* proxy_DeadObjectProxy_h */
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -120,36 +120,25 @@ ProxyObject::setSameCompartmentPrivate(c
{
MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
*slotOfPrivate() = priv;
}
void
ProxyObject::nuke()
{
- // When nuking scripted proxies, isCallable and isConstructor values for
- // the proxy needs to be preserved. Do this before clearing the target.
- uint32_t callable = handler()->isCallable(this);
- uint32_t constructor = handler()->isConstructor(this);
+ // Select a dead proxy handler based on the properties of this wrapper.
+ // Do this before clearing the target.
+ const BaseProxyHandler* handler = SelectDeadProxyHandler(this);
// Clear the target reference.
setSameCompartmentPrivate(NullValue());
// Update the handler to make this a DeadObjectProxy.
- if (callable) {
- if (constructor)
- setHandler(DeadObjectProxy<DeadProxyIsCallableIsConstructor>::singleton());
- else
- setHandler(DeadObjectProxy<DeadProxyIsCallableNotConstructor>::singleton());
- } else {
- if (constructor)
- setHandler(DeadObjectProxy<DeadProxyNotCallableIsConstructor>::singleton());
- else
- setHandler(DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton());
- }
+ setHandler(handler);
// The proxy's reserved slots are not cleared and will continue to be
// traced. This avoids the possibility of triggering write barriers while
// nuking proxies in dead compartments which could otherwise cause those
// compartments to be kept alive. Note that these are slots cannot hold
// cross compartment pointers, so this cannot cause the target compartment
// to leak.
}
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -185,21 +185,17 @@ WrapperFactory::PrepareForWrapping(JSCon
// compartment has been nuked, return a DeadObjectProxy to prevent further
// access.
JSCompartment* origin = js::GetObjectCompartment(obj);
JSCompartment* target = js::GetObjectCompartment(scope);
if (CompartmentPrivate::Get(origin)->wasNuked ||
CompartmentPrivate::Get(target)->wasNuked) {
NS_WARNING("Trying to create a wrapper into or out of a nuked compartment");
- RootedObject ccw(cx, Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton));
-
- NukeCrossCompartmentWrapper(cx, ccw);
-
- retObj.set(ccw);
+ retObj.set(JS_NewDeadWrapper(cx));
return;
}
// If we've got a WindowProxy, there's nothing special that needs to be
// done here, and we can move on to the next phase of wrapping. We handle
// this case first to allow us to assert against wrappers below.
if (js::IsWindowProxy(obj)) {