Bug 949220 - Make |NewProxyObject| return only non-singletons, and add |NewSingletonProxyObject| for the singleton case. r=jandem

Differential Revision: https://phabricator.services.mozilla.com/D70502

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jeff Walden 2020-04-14 16:57:15 +00:00
parent 5c128af468
commit 5f081085e8
14 changed files with 156 additions and 73 deletions

View File

@ -225,16 +225,16 @@ static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
// static
JSObject* WindowNamedPropertiesHandler::Create(JSContext* aCx,
JS::Handle<JSObject*> aProto) {
js::ProxyOptions options;
options.setClass(&WindowNamedPropertiesClass.mBase);
// Note: since the scope polluter proxy lives on the window's prototype
// chain, it needs a singleton type to avoid polluting type information
// for properties on the window.
js::ProxyOptions options;
options.setSingleton(true);
options.setClass(&WindowNamedPropertiesClass.mBase);
JS::Rooted<JSObject*> gsp(aCx);
gsp = js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
JS::NullHandleValue, aProto, options);
JS::Rooted<JSObject*> gsp(
aCx, js::NewSingletonProxyObject(
aCx, WindowNamedPropertiesHandler::getInstance(),
JS::NullHandleValue, aProto, options));
if (!gsp) {
return nullptr;
}

View File

@ -1297,12 +1297,11 @@ static JSObject* NewOuterWindowProxy(JSContext* cx,
js::WrapperOptions options;
options.setClass(&OuterWindowProxyClass);
options.setSingleton(true);
JSObject* obj =
js::Wrapper::New(cx, global,
isChrome ? &nsChromeOuterWindowProxy::singleton
: &nsOuterWindowProxy::singleton,
options);
js::Wrapper::NewSingleton(cx, global,
isChrome ? &nsChromeOuterWindowProxy::singleton
: &nsOuterWindowProxy::singleton,
options);
MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
return obj;
}

View File

@ -555,19 +555,11 @@ inline bool IsScriptedProxy(const JSObject* obj) {
class MOZ_STACK_CLASS ProxyOptions {
protected:
/* protected constructor for subclass */
explicit ProxyOptions(bool singletonArg, bool lazyProtoArg = false)
: singleton_(singletonArg),
lazyProto_(lazyProtoArg),
clasp_(&ProxyClass) {}
explicit ProxyOptions(bool lazyProtoArg)
: lazyProto_(lazyProtoArg), clasp_(&ProxyClass) {}
public:
ProxyOptions() : singleton_(false), lazyProto_(false), clasp_(&ProxyClass) {}
bool singleton() const { return singleton_; }
ProxyOptions& setSingleton(bool flag) {
singleton_ = flag;
return *this;
}
ProxyOptions() : ProxyOptions(false) {}
bool lazyProto() const { return lazyProto_; }
ProxyOptions& setLazyProto(bool flag) {
@ -582,7 +574,6 @@ class MOZ_STACK_CLASS ProxyOptions {
}
private:
bool singleton_;
bool lazyProto_;
const JSClass* clasp_;
};
@ -591,6 +582,10 @@ JS_FRIEND_API JSObject* NewProxyObject(
JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
JSObject* proto, const ProxyOptions& options = ProxyOptions());
JS_FRIEND_API JSObject* NewSingletonProxyObject(
JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
JSObject* proto, const ProxyOptions& options = ProxyOptions());
JSObject* RenewProxyObject(JSContext* cx, JSObject* obj,
BaseProxyHandler* handler, const Value& priv);

View File

@ -151,6 +151,10 @@ class JS_FRIEND_API Wrapper : public ForwardingProxyHandler {
static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
const WrapperOptions& options = WrapperOptions());
static JSObject* NewSingleton(
JSContext* cx, JSObject* obj, const Wrapper* handler,
const WrapperOptions& options = WrapperOptions());
static JSObject* Renew(JSObject* existing, JSObject* obj,
const Wrapper* handler);

View File

@ -300,7 +300,12 @@ extern "C" {
aObj: JS::HandleObject,
aHandler: *const ::libc::c_void,
aClass: *const JSClass,
aSingleton: bool,
) -> *mut JSObject;
pub fn WrapperNewSingleton(
aCx: *mut JSContext,
aObj: JS::HandleObject,
aHandler: *const ::libc::c_void,
aClass: *const JSClass,
) -> *mut JSObject;
pub fn NewWindowProxy(
aCx: *mut JSContext,

View File

@ -454,16 +454,24 @@ JSObject* NewProxyObject(JSContext* aCx, const void* aHandler,
}
JSObject* WrapperNew(JSContext* aCx, JS::HandleObject aObj,
const void* aHandler, const JSClass* aClass,
bool aSingleton) {
const void* aHandler, const JSClass* aClass) {
js::WrapperOptions options;
if (aClass) {
options.setClass(aClass);
}
options.setSingleton(aSingleton);
return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
}
JSObject* WrapperNewSingleton(JSContext* aCx, JS::HandleObject aObj,
const void* aHandler, const JSClass* aClass) {
js::WrapperOptions options;
if (aClass) {
options.setClass(aClass);
}
return js::Wrapper::NewSingleton(aCx, aObj, (const js::Wrapper*)aHandler,
options);
}
const JSClass WindowProxyClass = PROXY_CLASS_DEF(
"Proxy", JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */
@ -479,7 +487,7 @@ void SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val) {
JSObject* NewWindowProxy(JSContext* aCx, JS::HandleObject aObj,
const void* aHandler) {
return WrapperNew(aCx, aObj, aHandler, &WindowProxyClass, true);
return WrapperNewSingleton(aCx, aObj, aHandler, &WindowProxyClass);
}
JS::Value GetProxyPrivate(JSObject* obj) { return js::GetProxyPrivate(obj); }

View File

@ -385,10 +385,9 @@ ModuleNamespaceObject* ModuleNamespaceObject::create(
RootedValue priv(cx, ObjectValue(*module));
ProxyOptions options;
options.setLazyProto(true);
options.setSingleton(true);
Rooted<UniquePtr<IndirectBindingMap>> rootedBindings(cx, std::move(bindings));
RootedObject object(
cx, NewProxyObject(cx, &proxyHandler, priv, nullptr, options));
cx, NewSingletonProxyObject(cx, &proxyHandler, priv, nullptr, options));
if (!object) {
return nullptr;
}

View File

@ -45,9 +45,9 @@ BEGIN_TEST(testBug604087) {
js::WrapperOptions options;
options.setClass(&OuterWrapperClass);
options.setSingleton(true);
JS::RootedObject outerObj(
cx, js::Wrapper::New(cx, global, &js::Wrapper::singleton, options));
cx,
js::Wrapper::NewSingleton(cx, global, &js::Wrapper::singleton, options));
JS::RealmOptions globalOptions;
JS::RootedObject compartment2(
cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
@ -78,7 +78,8 @@ BEGIN_TEST(testBug604087) {
JS::RootedObject next(cx);
{
JSAutoRealm ar(cx, compartment2);
next = js::Wrapper::New(cx, compartment2, &js::Wrapper::singleton, options);
next = js::Wrapper::NewSingleton(cx, compartment2, &js::Wrapper::singleton,
options);
CHECK(next);
}

View File

@ -792,7 +792,32 @@ JS_FRIEND_API JSObject* js::NewProxyObject(JSContext* cx,
proto_ = TaggedProto::LazyProto;
}
return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
return ProxyObject::New(cx, handler, priv, TaggedProto(proto_),
options.clasp());
}
JS_FRIEND_API JSObject* js::NewSingletonProxyObject(
JSContext* cx, const BaseProxyHandler* handler, HandleValue priv,
JSObject* proto_, const ProxyOptions& options) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
// This can be called from the compartment wrap hooks while in a realm with a
// gray global. Trigger the read barrier on the global to ensure this is
// unmarked.
cx->realm()->maybeGlobal();
if (proto_ != TaggedProto::LazyProto) {
cx->check(proto_); // |priv| might be cross-compartment.
}
if (options.lazyProto()) {
MOZ_ASSERT(!proto_);
proto_ = TaggedProto::LazyProto;
}
return ProxyObject::NewSingleton(cx, handler, priv, TaggedProto(proto_),
options.clasp());
}
void ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv) {

View File

@ -282,6 +282,19 @@ JSObject* Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
return NewProxyObject(cx, handler, priv, options.proto(), options);
}
JSObject* Wrapper::NewSingleton(JSContext* cx, JSObject* obj,
const Wrapper* handler,
const WrapperOptions& options) {
// If this is a cross-compartment wrapper allocate it in the compartment's
// first global. See Compartment::globalForNewCCW.
mozilla::Maybe<AutoRealm> ar;
if (handler->isCrossCompartmentWrapper()) {
ar.emplace(cx, &cx->compartment()->globalForNewCCW());
}
RootedValue priv(cx, ObjectValue(*obj));
return NewSingletonProxyObject(cx, handler, priv, options.proto(), options);
}
JSObject* Wrapper::Renew(JSObject* existing, JSObject* obj,
const Wrapper* handler) {
existing->as<ProxyObject>().renew(handler, ObjectValue(*obj));

View File

@ -562,11 +562,10 @@ JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
js::WrapperOptions options;
options.setClass(&ShellWindowProxyClass);
options.setSingleton(true);
JSAutoRealm ar(cx, global);
JSObject* obj =
js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
js::Wrapper::NewSingleton(cx, global, &js::Wrapper::singleton, options);
MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
return obj;
}

View File

@ -1378,9 +1378,6 @@ JSObject* js::CloneObject(JSContext* cx, HandleObject obj,
obj->as<NativeObject>().getPrivate());
}
} else {
ProxyOptions options;
options.setClass(obj->getClass());
auto* handler = GetProxyHandler(obj);
// Same as above, require tenure allocation of the clone. This means for
@ -1391,7 +1388,8 @@ JSObject* js::CloneObject(JSContext* cx, HandleObject obj,
return nullptr;
}
clone = ProxyObject::New(cx, handler, JS::NullHandleValue, proto, options);
clone = ProxyObject::New(cx, handler, JS::NullHandleValue, proto,
obj->getClass());
if (!clone) {
return nullptr;
}

View File

@ -45,14 +45,29 @@ static gc::AllocKind GetProxyGCObjectKind(const JSClass* clasp,
return kind;
}
void ProxyObject::init(const BaseProxyHandler* handler, HandleValue priv,
JSContext* cx) {
setInlineValueArray();
detail::ProxyValueArray* values = detail::GetProxyDataLayout(this)->values();
values->init(numReservedSlots());
data.handler = handler;
if (IsCrossCompartmentWrapper(this)) {
MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW());
setCrossCompartmentPrivate(priv);
} else {
setSameCompartmentPrivate(priv);
}
}
/* static */
ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
HandleValue priv, TaggedProto proto_,
const ProxyOptions& options) {
const JSClass* clasp) {
Rooted<TaggedProto> proto(cx, proto_);
const JSClass* clasp = options.clasp();
#ifdef DEBUG
MOZ_ASSERT(isValidProxyClass(clasp));
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
@ -71,7 +86,7 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
* because we want to be able to keep track of them in typesets in useful
* ways.
*/
if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
if (proto.isObject() && !clasp->isDOMClass()) {
ObjectGroupRealm& realm = ObjectGroupRealm::getForNewObject(cx);
RootedObject protoObj(cx, proto.toObject());
if (!JSObject::setNewGroupUnknown(cx, realm, clasp, protoObj)) {
@ -82,12 +97,8 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
// Ensure that the wrapper has the same lifetime assumptions as the
// wrappee. Prefer to allocate in the nursery, when possible.
NewObjectKind newKind = NurseryAllocatedProxy;
if (options.singleton()) {
MOZ_ASSERT(priv.isNull() ||
(priv.isGCThing() && priv.toGCThing()->isTenured()));
newKind = SingletonObject;
} else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
!handler->canNurseryAllocate()) {
if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
!handler->canNurseryAllocate()) {
newKind = TenuredObject;
}
@ -100,28 +111,9 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
JS_TRY_VAR_OR_RETURN_NULL(cx, proxy,
create(cx, clasp, proto, allocKind, newKind));
proxy->setInlineValueArray();
proxy->init(handler, priv, cx);
detail::ProxyValueArray* values = detail::GetProxyDataLayout(proxy)->values();
values->init(proxy->numReservedSlots());
proxy->data.handler = handler;
if (IsCrossCompartmentWrapper(proxy)) {
MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW());
proxy->setCrossCompartmentPrivate(priv);
} else {
proxy->setSameCompartmentPrivate(priv);
}
if (newKind == SingletonObject) {
Rooted<ProxyObject*> rootedProxy(cx, proxy);
if (!JSObject::setSingleton(cx, rootedProxy)) {
return nullptr;
}
return rootedProxy;
}
/* Don't track types of properties of non-DOM and non-singleton proxies. */
// Don't track types of properties of non-DOM and non-singleton proxies.
if (!clasp->isDOMClass()) {
MarkObjectGroupUnknownProperties(cx, proxy->group());
}
@ -129,6 +121,43 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
return proxy;
}
/* static */
ProxyObject* ProxyObject::NewSingleton(JSContext* cx,
const BaseProxyHandler* handler,
HandleValue priv, TaggedProto proto_,
const JSClass* clasp) {
Rooted<TaggedProto> proto(cx, proto_);
#ifdef DEBUG
MOZ_ASSERT(isValidProxyClass(clasp));
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
MOZ_ASSERT_IF(proto.isObject(),
cx->compartment() == proto.toObject()->compartment());
MOZ_ASSERT(clasp->hasFinalize());
if (priv.isGCThing()) {
JS::AssertCellIsNotGray(priv.toGCThing());
}
#endif
gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
AutoSetNewObjectMetadata metadata(cx);
// Note: this will initialize the object's |data| to strange values, but we
// will immediately overwrite those below.
ProxyObject* proxy;
JS_TRY_VAR_OR_RETURN_NULL(
cx, proxy, create(cx, clasp, proto, allocKind, SingletonObject));
proxy->init(handler, priv, cx);
Rooted<ProxyObject*> rootedProxy(cx, proxy);
if (!JSObject::setSingleton(cx, rootedProxy)) {
return nullptr;
}
return rootedProxy;
}
gc::AllocKind ProxyObject::allocKindForTenure() const {
MOZ_ASSERT(usingInlineValueArray());
Value priv = private_();

View File

@ -46,7 +46,14 @@ class ProxyObject : public JSObject {
public:
static ProxyObject* New(JSContext* cx, const BaseProxyHandler* handler,
HandleValue priv, TaggedProto proto_,
const ProxyOptions& options);
const JSClass* clasp);
static ProxyObject* NewSingleton(JSContext* cx,
const BaseProxyHandler* handler,
HandleValue priv, TaggedProto proto_,
const JSClass* clasp);
void init(const BaseProxyHandler* handler, HandleValue priv, JSContext* cx);
// Proxies usually store their ProxyValueArray inline in the object.
// There's one unfortunate exception: when a proxy is swapped with another
@ -62,6 +69,7 @@ class ProxyObject : public JSObject {
&reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart())
->reservedSlots;
}
MOZ_MUST_USE bool initExternalValueArrayAfterSwap(JSContext* cx,
HandleValueVector values);