author | Ryan VanderMeulen <ryanvm@gmail.com> |
Tue, 06 Jan 2015 19:07:06 -0500 | |
changeset 222298 | 33781a3a5201c2371d5d31eeaaaeb5f34a3aaced |
parent 222297 | e3e24120107b3250c993a4ccccfcd7fbb3132351 |
child 222299 | 6ccaa9fe342379c19b5e56c55bef3c37e22e0b7c |
child 222393 | 4baccd81ced8949fea9452d1098db3ad75d8b684 |
child 222421 | a185d1c79df9f53255915f113d2d11c26b32a7cf |
push id | 53582 |
push user | ryanvm@gmail.com |
push date | Wed, 07 Jan 2015 00:07:13 +0000 |
treeherder | mozilla-inbound@33781a3a5201 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1055755 |
milestone | 37.0a1 |
backs out | 205f8fa00772c35ad26f9320639988e27dfae247 |
first release with | nightly linux32
33781a3a5201
/
37.0a1
/
20150107030217
/
files
nightly linux64
33781a3a5201
/
37.0a1
/
20150107030217
/
files
nightly mac
33781a3a5201
/
37.0a1
/
20150107030217
/
files
nightly win32
33781a3a5201
/
37.0a1
/
20150107030217
/
files
nightly win64
33781a3a5201
/
37.0a1
/
20150107030217
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
37.0a1
/
20150107030217
/
pushlog to previous
nightly linux64
37.0a1
/
20150107030217
/
pushlog to previous
nightly mac
37.0a1
/
20150107030217
/
pushlog to previous
nightly win32
37.0a1
/
20150107030217
/
pushlog to previous
nightly win64
37.0a1
/
20150107030217
/
pushlog to previous
|
--- a/js/src/jsapi-tests/testBug604087.cpp +++ b/js/src/jsapi-tests/testBug604087.cpp @@ -39,17 +39,18 @@ static JSObject * PreWrap(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj, JS::HandleObject objectPassedToWrap) { JS_GC(JS_GetRuntime(cx)); return obj; } static JSObject * -Wrap(JSContext *cx, JS::HandleObject obj, JS::HandleObject parent) +Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj, + JS::HandleObject parent) { return js::Wrapper::New(cx, obj, parent, &js::CrossCompartmentWrapper::singleton); } static const JSWrapObjectCallbacks WrapObjectCallbacks = { Wrap, PreWrap };
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -796,20 +796,25 @@ typedef bool JS::MutableHandleValue rval); typedef bool (* JSLocaleToUnicode)(JSContext *cx, const char *src, JS::MutableHandleValue rval); /* * 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. |obj| is the object to be wrapped. + * destination compartment. |obj| is the object to be wrapped. If |existing| is + * non-nullptr, 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, JS::HandleObject obj, JS::HandleObject parent); +(* JSWrapObjectCallback)(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj, + JS::HandleObject parent); /* * Callback used by the wrap hook to ask the embedding to prepare an object * for wrapping in a context. This might include unwrapping other wrappers * or even finding a more suitable object for the new compartment. */ typedef JSObject * (* JSPreWrapCallback)(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj,
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -338,20 +338,22 @@ JSCompartment::wrap(JSContext *cx, Mutab if (!putWrapper(cx, CrossCompartmentKey(key), StringValue(copy))) return false; strp.set(copy); return true; } bool -JSCompartment::wrap(JSContext *cx, MutableHandleObject obj) +JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); + MOZ_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); + MOZ_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); // Wrappers should really be parented to the wrapped parent of the wrapped // object, but in that case a wrapped global object would have a nullptr // parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, @@ -414,17 +416,30 @@ JSCompartment::wrap(JSContext *cx, Mutab RootedValue key(cx, ObjectValue(*obj)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) { obj.set(&p->value().get().toObject()); MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>()); MOZ_ASSERT(obj->getParent() == global); return true; } - obj.set(cb->wrap(cx, obj, global)); + RootedObject existing(cx, existingArg); + if (existing) { + // Is it possible to reuse |existing|? + if (!existing->getTaggedProto().isLazy() || + // Note: Class asserted above, so all that's left to check is callability + existing->isCallable() || + existing->getParent() != global || + obj->isCallable()) + { + existing = nullptr; + } + } + + obj.set(cb->wrap(cx, existing, obj, global)); if (!obj) return false; // We maintain the invariant that the key in the cross-compartment wrapper // map is always directly wrapped by the value. MOZ_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); return putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*obj));
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -350,20 +350,22 @@ struct JSCompartment JSCompartment(JS::Zone *zone, const JS::CompartmentOptions &options); ~JSCompartment(); bool init(JSContext *cx); /* Mark cross-compartment wrappers. */ void markCrossCompartmentWrappers(JSTracer *trc); - inline bool wrap(JSContext *cx, JS::MutableHandleValue vp); + inline bool wrap(JSContext *cx, JS::MutableHandleValue vp, + JS::HandleObject existing = js::NullPtr()); bool wrap(JSContext *cx, js::MutableHandleString strp); - bool wrap(JSContext *cx, JS::MutableHandleObject obj); + bool wrap(JSContext *cx, JS::MutableHandleObject obj, + JS::HandleObject existingArg = js::NullPtr()); bool wrap(JSContext *cx, JS::MutableHandle<js::PropertyDescriptor> desc); bool wrap(JSContext *cx, JS::MutableHandle<js::PropDesc> desc); template<typename T> bool wrap(JSContext *cx, JS::AutoVectorRooter<T> &vec) { for (size_t i = 0; i < vec.length(); ++i) { if (!wrap(cx, vec[i])) return false; }
--- a/js/src/jscompartmentinlines.h +++ b/js/src/jscompartmentinlines.h @@ -49,18 +49,20 @@ js::AutoCompartment::AutoCompartment(Exc } js::AutoCompartment::~AutoCompartment() { cx_->leaveCompartment(origin_); } inline bool -JSCompartment::wrap(JSContext *cx, JS::MutableHandleValue vp) +JSCompartment::wrap(JSContext *cx, JS::MutableHandleValue vp, JS::HandleObject existing) { + MOZ_ASSERT_IF(existing, vp.isObject()); + /* Only GC things have to be wrapped or copied. */ if (!vp.isMarkable()) return true; /* * Symbols are GC things, but never need to be wrapped or copied because * they are always allocated in the atoms compartment. */ @@ -107,16 +109,16 @@ JSCompartment::wrap(JSContext *cx, JS::M cacheResult = &p->value().get().toObject(); #else vp.set(p->value()); return true; #endif } JS::RootedObject obj(cx, &vp.toObject()); - if (!wrap(cx, &obj)) + if (!wrap(cx, &obj, existing)) return false; vp.setObject(*obj); MOZ_ASSERT_IF(cacheResult, obj == cacheResult); return true; } #endif /* jscompartmentinlines_h */
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -67,17 +67,17 @@ class JS_FRIEND_API(Wrapper) : public Di }; virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const MOZ_OVERRIDE; static JSObject *New(JSContext *cx, JSObject *obj, JSObject *parent, const Wrapper *handler, const WrapperOptions &options = WrapperOptions()); - static JSObject *Renew(JSContext *cx, JSObject *obj, const Wrapper *handler); + static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, const Wrapper *handler); static const Wrapper *wrapperHandler(JSObject *wrapper); static JSObject *wrappedObject(JSObject *wrapper); unsigned flags() const { return mFlags; } @@ -212,17 +212,18 @@ class JS_FRIEND_API(SecurityWrapper) : p typedef Base Permissive; typedef SecurityWrapper<Base> Restrictive; }; typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper; typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper; extern JSObject * -TransparentObjectWrapper(JSContext *cx, HandleObject obj, HandleObject parent); +TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, + HandleObject parent); inline bool IsWrapper(JSObject *obj) { return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family; } // Given a JSObject, returns that object stripped of wrappers. If
--- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -521,27 +521,35 @@ js::RemapWrapper(JSContext *cx, JSObject WrapperMap::Ptr p = wcompartment->lookupWrapper(origv); MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj); wcompartment->removeWrapper(p); // When we remove origv from the wrapper map, its wrapper, wobj, must // immediately cease to be a cross-compartment wrapper. Neuter it. NukeCrossCompartmentWrapper(cx, wobj); - // First, we wrap it in the new compartment. + // 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. RootedObject tobj(cx, newTarget); AutoCompartment ac(cx, wobj); - if (!wcompartment->wrap(cx, &tobj)) + if (!wcompartment->wrap(cx, &tobj, wobj)) MOZ_CRASH(); - // 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 (!JSObject::swap(cx, wobj, tobj)) - MOZ_CRASH(); + // 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 (!JSObject::swap(cx, wobj, tobj)) + MOZ_CRASH(); + } // Before swapping, this wrapper came out of wrap(), which enforces the // invariant that the wrapper in the map points directly to the key. MOZ_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). MOZ_ASSERT(wobj->is<WrapperObject>());
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -828,16 +828,31 @@ const Class* const js::ProxyClassPtr = & JS_FRIEND_API(JSObject *) js::NewProxyObject(JSContext *cx, const BaseProxyHandler *handler, HandleValue priv, JSObject *proto_, JSObject *parent_, const ProxyOptions &options) { return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), parent_, options); } +void +ProxyObject::renew(JSContext *cx, const BaseProxyHandler *handler, Value priv) +{ + MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this)); + MOZ_ASSERT(getParent() == cx->global()); + MOZ_ASSERT(getClass() == &ProxyObject::class_); + MOZ_ASSERT(!getClass()->ext.innerObject); + MOZ_ASSERT(hasLazyPrototype()); + + setHandler(handler); + setCrossCompartmentPrivate(priv); + setExtra(0, UndefinedValue()); + setExtra(1, UndefinedValue()); +} + JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, HandleObject obj) { static const JSFunctionSpec static_methods[] = { JS_FN("create", proxy_create, 2, 0), JS_FN("createFunction", proxy_createFunction, 3, 0), JS_FN("revocable", proxy_revocable, 2, 0), JS_FS_END
--- a/js/src/proxy/Wrapper.cpp +++ b/js/src/proxy/Wrapper.cpp @@ -37,16 +37,23 @@ Wrapper::New(JSContext *cx, JSObject *ob const WrapperOptions &options) { MOZ_ASSERT(parent); RootedValue priv(cx, ObjectValue(*obj)); return NewProxyObject(cx, handler, priv, options.proto(), parent, options); } +JSObject * +Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, const Wrapper *handler) +{ + existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj)); + return existing; +} + const Wrapper * Wrapper::wrapperHandler(JSObject *wrapper) { MOZ_ASSERT(wrapper->is<WrapperObject>()); return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler()); } JSObject * @@ -115,17 +122,18 @@ js::UnwrapOneChecked(JSObject *obj, bool const char Wrapper::family = 0; const Wrapper Wrapper::singleton((unsigned)0); const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true); JSObject *Wrapper::defaultProto = TaggedProto::LazyProto; /* Compartments. */ extern JSObject * -js::TransparentObjectWrapper(JSContext *cx, HandleObject obj, HandleObject parent) +js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, + HandleObject parent) { // Allow wrapping outer window proxies. MOZ_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject); return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton); } ErrorCopier::~ErrorCopier() {
--- a/js/src/vm/ProxyObject.h +++ b/js/src/vm/ProxyObject.h @@ -94,16 +94,18 @@ class ProxyObject : public JSObject (clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS) && clasp->trace == proxy_Trace && !clasp->call && !clasp->construct; } public: static unsigned grayLinkExtraSlot(JSObject *obj); + void renew(JSContext *cx, const BaseProxyHandler *handler, Value priv); + static void trace(JSTracer *trc, JSObject *obj); void nuke(const BaseProxyHandler *handler); static const Class class_; }; } // namespace js
--- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -377,17 +377,18 @@ SelectAddonWrapper(JSContext *cx, Handle else if (wrapper == &PermissiveXrayDOM::singleton) return &AddonWrapper<PermissiveXrayDOM>::singleton; // |wrapper| is not supported for interposition, so we don't do it. return wrapper; } JSObject * -WrapperFactory::Rewrap(JSContext *cx, HandleObject obj, HandleObject parent) +WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, + HandleObject parent) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before @@ -490,16 +491,19 @@ WrapperFactory::Rewrap(JSContext *cx, Ha NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); + if (existing) + return Wrapper::Renew(cx, existing, obj, wrapper); + return Wrapper::New(cx, obj, parent, wrapper); } // 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
--- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -40,16 +40,17 @@ class WrapperFactory { // Prepare a given object for wrapping in a new compartment. static JSObject *PrepareForWrapping(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj, JS::HandleObject objectPassedToWrap); // Rewrap an object that is about to cross compartment boundaries. static JSObject *Rewrap(JSContext *cx, + JS::HandleObject existing, JS::HandleObject obj, JS::HandleObject parent); // Wrap wrapped object into a waiver wrapper and then re-wrap it. static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp); static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleObject object); };