mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 803376 - Allow wrappers to be reused (r=bholley)
This commit is contained in:
parent
e3f39b6c2e
commit
89122b3c84
@ -56,7 +56,8 @@ PreWrap(JSContext *cx, JSObject *scopeArg, JSObject *objArg, unsigned flags)
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
Wrap(JSContext *cx, JSObject *objArg, JSObject *protoArg, JSObject *parentArg, unsigned flags)
|
||||
Wrap(JSContext *cx, JSObject *existing, JSObject *objArg,
|
||||
JSObject *protoArg, JSObject *parentArg, unsigned flags)
|
||||
{
|
||||
js::RootedObject obj(cx, objArg);
|
||||
js::RootedObject proto(cx, protoArg);
|
||||
|
@ -1950,10 +1950,15 @@ typedef JSBool
|
||||
/*
|
||||
* Callback used to ask the embedding for the cross compartment wrapper handler
|
||||
* that implements the desired prolicy for this kind of object in the
|
||||
* destination compartment.
|
||||
* destination compartment. |obj| is the object to be wrapped. If |existing| is
|
||||
* non-NULL, it will point to an existing wrapper object that should be re-used
|
||||
* if possible. |existing| is guaranteed to be a cross-compartment wrapper with
|
||||
* a lazily-defined prototype and the correct global. It is guaranteed not to
|
||||
* wrap a function.
|
||||
*/
|
||||
typedef JSObject *
|
||||
(* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
|
||||
(* JSWrapObjectCallback)(JSContext *cx, JSObject *existing, JSObject *obj,
|
||||
JSObject *proto, JSObject *parent,
|
||||
unsigned flags);
|
||||
|
||||
/*
|
||||
|
@ -193,9 +193,12 @@ WrapForSameCompartment(JSContext *cx, HandleObject obj, Value *vp)
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, Value *vp)
|
||||
JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
|
||||
{
|
||||
JS_ASSERT(cx->compartment == this);
|
||||
JS_ASSERT_IF(existing, existing->compartment() == cx->compartment);
|
||||
JS_ASSERT_IF(existing, vp->isObject());
|
||||
JS_ASSERT_IF(existing, IsDeadProxyObject(existing));
|
||||
|
||||
unsigned flags = 0;
|
||||
|
||||
@ -304,14 +307,25 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
||||
|
||||
RootedObject obj(cx, &vp->toObject());
|
||||
|
||||
/* See if we can reuse |existing| as the wrapper for |obj|. */
|
||||
JSObject *proto = Proxy::LazyProto;
|
||||
if (existing) {
|
||||
if (!existing->getTaggedProto().isLazy() ||
|
||||
existing->getClass() != &ObjectProxyClass ||
|
||||
existing->getParent() != global ||
|
||||
obj->isCallable())
|
||||
{
|
||||
existing = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We hand in the original wrapped object into the wrap hook to allow
|
||||
* the wrap hook to reason over what wrappers are currently applied
|
||||
* to the object.
|
||||
*/
|
||||
RootedObject wrapper(cx, cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags));
|
||||
RootedObject wrapper(cx);
|
||||
wrapper = cx->runtime->wrapObjectCallback(cx, existing, obj, proto, global, flags);
|
||||
if (!wrapper)
|
||||
return false;
|
||||
|
||||
@ -348,12 +362,12 @@ JSCompartment::wrap(JSContext *cx, HeapPtrString *strp)
|
||||
}
|
||||
|
||||
bool
|
||||
JSCompartment::wrap(JSContext *cx, JSObject **objp)
|
||||
JSCompartment::wrap(JSContext *cx, JSObject **objp, JSObject *existing)
|
||||
{
|
||||
if (!*objp)
|
||||
return true;
|
||||
RootedValue value(cx, ObjectValue(**objp));
|
||||
if (!wrap(cx, value.address()))
|
||||
if (!wrap(cx, value.address(), existing))
|
||||
return false;
|
||||
*objp = &value.get().toObject();
|
||||
return true;
|
||||
|
@ -349,10 +349,10 @@ struct JSCompartment
|
||||
/* Mark cross-compartment wrappers. */
|
||||
void markCrossCompartmentWrappers(JSTracer *trc);
|
||||
|
||||
bool wrap(JSContext *cx, js::Value *vp);
|
||||
bool wrap(JSContext *cx, js::Value *vp, JSObject *existing = NULL);
|
||||
bool wrap(JSContext *cx, JSString **strp);
|
||||
bool wrap(JSContext *cx, js::HeapPtrString *strp);
|
||||
bool wrap(JSContext *cx, JSObject **objp);
|
||||
bool wrap(JSContext *cx, JSObject **objp, JSObject *existing = NULL);
|
||||
bool wrapId(JSContext *cx, jsid *idp);
|
||||
bool wrap(JSContext *cx, js::PropertyOp *op);
|
||||
bool wrap(JSContext *cx, js::StrictPropertyOp *op);
|
||||
|
@ -3143,6 +3143,23 @@ js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv_,
|
||||
return NewProxyObject(cx, handler, priv_, TaggedProto(proto_), parent_, call_, construct_);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::RenewProxyObject(JSContext *cx, JSObject *obj,
|
||||
BaseProxyHandler *handler, Value priv)
|
||||
{
|
||||
JS_ASSERT(obj->getParent() == cx->global());
|
||||
JS_ASSERT(obj->getClass() == &ObjectProxyClass);
|
||||
JS_ASSERT(obj->getTaggedProto().isLazy());
|
||||
JS_ASSERT(!handler->isOuterWindow());
|
||||
|
||||
obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
|
||||
obj->setCrossCompartmentSlot(JSSLOT_PROXY_PRIVATE, priv);
|
||||
obj->setSlot(JSSLOT_PROXY_EXTRA + 0, UndefinedValue());
|
||||
obj->setSlot(JSSLOT_PROXY_EXTRA + 1, UndefinedValue());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
proxy(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
|
@ -318,6 +318,9 @@ NewProxyObject(JSContext *cx, BaseProxyHandler *handler, const Value &priv,
|
||||
JSObject *proto, JSObject *parent,
|
||||
JSObject *call = NULL, JSObject *construct = NULL);
|
||||
|
||||
JSObject *
|
||||
RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
extern JS_FRIEND_API(JSObject *)
|
||||
|
@ -56,6 +56,21 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
|
||||
obj->isCallable() ? obj : NULL, NULL);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler)
|
||||
{
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (obj->isXML()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CANT_WRAP_XML_OBJECT);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(!obj->isCallable());
|
||||
return RenewProxyObject(cx, existing, handler, ObjectValue(*obj));
|
||||
}
|
||||
|
||||
Wrapper *
|
||||
Wrapper::wrapperHandler(RawObject wrapper)
|
||||
{
|
||||
@ -357,7 +372,8 @@ Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
|
||||
/* Compartments. */
|
||||
|
||||
extern JSObject *
|
||||
js::TransparentObjectWrapper(JSContext *cx, JSObject *objArg, JSObject *wrappedProtoArg, JSObject *parentArg,
|
||||
js::TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *objArg,
|
||||
JSObject *wrappedProtoArg, JSObject *parentArg,
|
||||
unsigned flags)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
@ -967,6 +983,12 @@ js::NewDeadProxyObject(JSContext *cx, JSObject *parent)
|
||||
NULL, parent, NULL, NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
js::IsDeadProxyObject(RawObject obj)
|
||||
{
|
||||
return IsProxy(obj) && GetProxyHandler(obj) == &DeadObjectProxy::singleton;
|
||||
}
|
||||
|
||||
void
|
||||
js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper)
|
||||
{
|
||||
@ -1063,25 +1085,32 @@ js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
|
||||
// immediately cease to be a cross-compartment wrapper. Neuter it.
|
||||
NukeCrossCompartmentWrapper(cx, wobj);
|
||||
|
||||
// First, we wrap it in the new compartment. This will return
|
||||
// a new wrapper.
|
||||
// First, we wrap it in the new compartment. We try to use the existing
|
||||
// wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
|
||||
// the choice to reuse |wobj| or not.
|
||||
JSObject *tobj = newTarget;
|
||||
AutoCompartment ac(cx, wobj);
|
||||
if (!wcompartment->wrap(cx, &tobj))
|
||||
if (!wcompartment->wrap(cx, &tobj, wobj))
|
||||
return false;
|
||||
|
||||
// Now, because we need to maintain object identity, we do a
|
||||
// brain transplant on the old object. At the same time, we
|
||||
// update the entry in the compartment's wrapper map to point
|
||||
// to the old wrapper.
|
||||
JS_ASSERT(tobj != wobj);
|
||||
if (!wobj->swap(cx, tobj))
|
||||
return false;
|
||||
// If wrap() reused |wobj|, it will have overwritten it and returned with
|
||||
// |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
|
||||
// will still be nuked. In the latter case, we replace |wobj| with the
|
||||
// contents of the new wrapper in |tobj|.
|
||||
if (tobj != wobj) {
|
||||
// Now, because we need to maintain object identity, we do a brain
|
||||
// transplant on the old object so that it contains the contents of the
|
||||
// new one.
|
||||
if (!wobj->swap(cx, tobj))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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).
|
||||
pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj));
|
||||
return true;
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
|
||||
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto,
|
||||
JSObject *parent, Wrapper *handler);
|
||||
|
||||
static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler);
|
||||
|
||||
static Wrapper *wrapperHandler(RawObject wrapper);
|
||||
|
||||
static JSObject *wrappedObject(RawObject wrapper);
|
||||
@ -235,7 +237,8 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
|
||||
};
|
||||
|
||||
extern JSObject *
|
||||
TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
|
||||
TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *obj,
|
||||
JSObject *wrappedProto, JSObject *parent,
|
||||
unsigned flags);
|
||||
|
||||
// Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by
|
||||
@ -270,6 +273,9 @@ UnwrapOneChecked(JSContext *cx, HandleObject obj);
|
||||
JS_FRIEND_API(bool)
|
||||
IsCrossCompartmentWrapper(RawObject obj);
|
||||
|
||||
bool
|
||||
IsDeadProxyObject(RawObject obj);
|
||||
|
||||
JSObject *
|
||||
NewDeadProxyObject(JSContext *cx, JSObject *parent);
|
||||
|
||||
|
@ -292,7 +292,8 @@ GetWrappedNative(JSContext *cx, JSObject *obj)
|
||||
}
|
||||
|
||||
JSObject *
|
||||
WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
|
||||
WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj,
|
||||
JSObject *wrappedProto, JSObject *parent,
|
||||
unsigned flags)
|
||||
{
|
||||
NS_ASSERTION(!IsWrapper(obj) ||
|
||||
@ -448,6 +449,9 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
|
||||
}
|
||||
}
|
||||
|
||||
if (existing && proxyProto == wrappedProto)
|
||||
return Wrapper::Renew(cx, existing, obj, wrapper);
|
||||
|
||||
return Wrapper::New(cx, obj, proxyProto, parent, wrapper);
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ class WrapperFactory {
|
||||
|
||||
// Rewrap an object that is about to cross compartment boundaries.
|
||||
static JSObject *Rewrap(JSContext *cx,
|
||||
JSObject *existing,
|
||||
JSObject *obj,
|
||||
JSObject *wrappedProto,
|
||||
JSObject *parent,
|
||||
|
Loading…
Reference in New Issue
Block a user