Bug 1719795 part 5 - Change JSCLASS_PRIVATE_IS_NSISUPPORTS JSClasses to use a reserved slot instead. r=mccr8,jonco

This is a step towards removing object private slots.

Classes with JSCLASS_PRIVATE_IS_NSISUPPORTS now use JSCLASS_SLOT0_IS_NSISUPPORTS
instead. For most classes this means we need to add an extra reserved slot and remove
the private slot.

Global objects (SimpleGlobalObject and the XPConnect BackstagePass and Sandbox globals)
however can use the JSCLASS_GLOBAL_APPLICATION_SLOTS already there. These slots were
only used for WebIDL DOM globals until now.

Differential Revision: https://phabricator.services.mozilla.com/D119502
This commit is contained in:
Jan de Mooij 2021-07-13 09:31:34 +00:00
Родитель 5285ac89a9
Коммит 59f8590115
20 изменённых файлов: 117 добавлений и 73 удалений

Просмотреть файл

@ -8,7 +8,7 @@
#include "jsapi.h"
#include "js/Class.h"
#include "js/Object.h" // JS::GetClass, JS::GetPrivate, JS::SetPrivate
#include "js/Object.h" // JS::GetClass, JS::GetObjectISupports, JS::SetObjectISupports
#include "nsJSPrincipals.h"
#include "nsThreadUtils.h"
@ -76,9 +76,12 @@ static const JSClassOps SimpleGlobalClassOps = {
static const js::ClassExtension SimpleGlobalClassExtension = {
SimpleGlobal_moved};
static_assert(JSCLASS_GLOBAL_APPLICATION_SLOTS > 0,
"Need at least one slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
const JSClass SimpleGlobalClass = {"",
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE |
JSCLASS_PRIVATE_IS_NSISUPPORTS |
JSCLASS_GLOBAL_FLAGS |
JSCLASS_SLOT0_IS_NSISUPPORTS |
JSCLASS_FOREGROUND_FINALIZE,
&SimpleGlobalClassOps,
JS_NULL_CLASS_SPEC,
@ -88,7 +91,7 @@ const JSClass SimpleGlobalClass = {"",
static SimpleGlobalObject* GetSimpleGlobal(JSObject* global) {
MOZ_ASSERT(JS::GetClass(global) == &SimpleGlobalClass);
return static_cast<SimpleGlobalObject*>(JS::GetPrivate(global));
return JS::GetObjectISupports<SimpleGlobalObject>(global);
}
// static
@ -139,7 +142,7 @@ JSObject* SimpleGlobalObject::Create(GlobalType globalType,
new SimpleGlobalObject(global, globalType);
// Pass on ownership of globalObject to |global|.
JS::SetPrivate(global, globalObject.forget().take());
JS::SetObjectISupports(global, globalObject.forget().take());
if (proto.isObjectOrNull()) {
JS::Rooted<JSObject*> protoObj(cx, proto.toObjectOrNull());

Просмотреть файл

@ -500,8 +500,8 @@ static const uint32_t JSCLASS_DELAY_METADATA_BUILDER = 1 << 1;
// disposal mechanism.
static const uint32_t JSCLASS_IS_WRAPPED_NATIVE = 1 << 2;
// Private is `nsISupports*`.
static const uint32_t JSCLASS_PRIVATE_IS_NSISUPPORTS = 1 << 3;
// First reserved slot is `PrivateValue(nsISupports*)` or `UndefinedValue`.
static constexpr uint32_t JSCLASS_SLOT0_IS_NSISUPPORTS = 1 << 3;
// Objects are DOM.
static const uint32_t JSCLASS_IS_DOMJSCLASS = 1 << 4;
@ -706,6 +706,8 @@ struct alignas(js::gc::JSClassAlignBytes) JSClass {
bool isWrappedNative() const { return flags & JSCLASS_IS_WRAPPED_NATIVE; }
bool slot0IsISupports() const { return flags & JSCLASS_SLOT0_IS_NSISUPPORTS; }
static size_t offsetOfFlags() { return offsetof(JSClass, flags); }
// Internal / friend API accessors:

Просмотреть файл

@ -112,6 +112,31 @@ inline void SetReservedSlot(JSObject* obj, size_t slot, const Value& value) {
}
}
/**
* Helper function to get the pointer value (or nullptr if not set) from the
* object's first reserved slot. Must only be used for objects with a JSClass
* that has the JSCLASS_SLOT0_IS_NSISUPPORTS flag.
*/
template <typename T>
inline T* GetObjectISupports(JSObject* obj) {
MOZ_ASSERT(GetClass(obj)->slot0IsISupports());
Value v = GetReservedSlot(obj, 0);
return v.isUndefined() ? nullptr : static_cast<T*>(v.toPrivate());
}
/**
* Helper function to store |PrivateValue(nsISupportsValue)| in the object's
* first reserved slot. Must only be used for objects with a JSClass that has
* the JSCLASS_SLOT0_IS_NSISUPPORTS flag.
*
* Note: the pointer is opaque to the JS engine (including the GC) so it's the
* embedding's responsibility to trace or free this value.
*/
inline void SetObjectISupports(JSObject* obj, void* nsISupportsValue) {
MOZ_ASSERT(GetClass(obj)->slot0IsISupports());
SetReservedSlot(obj, 0, PrivateValue(nsISupportsValue));
}
} // namespace JS
#endif // js_public_Object_h

Просмотреть файл

@ -23,7 +23,7 @@
#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStream{BYOB,Default}Reader, js::ForAuthorCodeBool
#include "builtin/streams/WritableStream.h" // js::WritableStream
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
#include "js/Class.h" // JSCLASS_PRIVATE_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
#include "js/Class.h" // JSCLASS_SLOT0_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
#include "js/Conversions.h" // JS::ToBoolean
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h" // JS{Function,Property}Spec, JS_FN, JS_PSG, JS_{FS,PS}_END
@ -543,7 +543,7 @@ static bool FinishReadableStreamClassInit(JSContext* cx, Handle<JSObject*> ctor,
// This function and everything below should be replaced with
//
// JS_STREAMS_CLASS_SPEC(ReadableStream, 0, SlotCount, 0,
// JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE,
// JSCLASS_SLOT0_IS_NSISUPPORTS,
// JS_NULL_CLASS_OPS);
//
// when "pipeTo" is always enabled.
@ -575,7 +575,7 @@ const JSClass ReadableStream::class_ = {
"ReadableStream",
JSCLASS_HAS_RESERVED_SLOTS(ReadableStream::SlotCount) |
JSCLASS_HAS_CACHED_PROTO(JSProto_ReadableStream) |
JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE,
JSCLASS_SLOT0_IS_NSISUPPORTS,
JS_NULL_CLASS_OPS, &ReadableStream::classSpec_};
const JSClass ReadableStream::protoClass_ = {

Просмотреть файл

@ -43,6 +43,12 @@ class ReadableStream : public NativeObject {
* stream's constructor and thus cannot be in a different compartment.
*/
enum Slots {
/**
* Optional pointer to make the stream participate in Gecko's cycle
* collection. See also JSCLASS_SLOT0_IS_NSISUPPORTS.
*/
Slot_ISupports,
Slot_Controller,
Slot_Reader,
Slot_State,

Просмотреть файл

@ -135,7 +135,9 @@ using JS::Value;
return nullptr;
}
stream->setPrivate(nsISupportsObject_alreadyAddreffed);
static_assert(Slot_ISupports == 0,
"Must use right slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
JS::SetObjectISupports(stream, nsISupportsObject_alreadyAddreffed);
// Step 1: Set stream.[[state]] to "readable".
stream->initStateBits(Readable);

Просмотреть файл

@ -26,7 +26,7 @@
#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/GCAPI.h" // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis
#include "js/Object.h" // JS::SetPrivate
#include "js/Object.h" // JS::SetObjectISupports
#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted
#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource
#include "js/Value.h" // JS::{,Object,Undefined}Value
@ -399,7 +399,7 @@ JS_PUBLIC_API bool JS::ReadableStreamUpdateDataAvailableFromSource(
JS_PUBLIC_API void JS::ReadableStreamReleaseCCObject(JSObject* streamObj) {
MOZ_ASSERT(JS::IsReadableStream(streamObj));
JS::SetPrivate(streamObj, nullptr);
JS::SetObjectISupports(streamObj, nullptr);
}
JS_PUBLIC_API bool JS::ReadableStreamTee(JSContext* cx,

Просмотреть файл

@ -19,7 +19,7 @@
#include "builtin/streams/WritableStreamDefaultWriter.h" // js::CreateWritableStreamDefaultWriter
#include "builtin/streams/WritableStreamOperations.h" // js::WritableStream{Abort,Close{,QueuedOrInFlight}}
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
#include "js/Class.h" // JS{Function,Property}Spec, JS_{FS,PS}_END, JSCLASS_PRIVATE_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
#include "js/Class.h" // JS{Function,Property}Spec, JS_{FS,PS}_END, JSCLASS_SLOT0_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/RealmOptions.h" // JS::RealmCreationOptions
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
@ -277,5 +277,4 @@ static const JSPropertySpec WritableStream_properties[] = {
JS_PSG("locked", WritableStream_locked, 0), JS_PS_END};
JS_STREAMS_CLASS_SPEC(WritableStream, 0, SlotCount, 0,
JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE,
JS_NULL_CLASS_OPS);
JSCLASS_SLOT0_IS_NSISUPPORTS, JS_NULL_CLASS_OPS);

Просмотреть файл

@ -33,6 +33,12 @@ class WritableStreamDefaultWriter;
class WritableStream : public NativeObject {
public:
enum Slots {
/**
* Optional pointer to make the stream participate in Gecko's cycle
* collection. See also JSCLASS_SLOT0_IS_NSISUPPORTS.
*/
Slot_ISupports,
/**
* A WritableStream's associated controller is always created from under the
* stream's constructor and thus cannot be in a different compartment.

Просмотреть файл

@ -76,7 +76,9 @@ using JS::Value;
return nullptr;
}
stream->setPrivate(nsISupportsObject_alreadyAddreffed);
static_assert(Slot_ISupports == 0,
"Must use right slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
JS::SetObjectISupports(stream, nsISupportsObject_alreadyAddreffed);
stream->initWritableState();

Просмотреть файл

@ -99,29 +99,29 @@ extern const js::ClassExtension XPC_WN_JSClassExtension;
: XPCWrappedNative_Trace, \
}
#define XPC_MAKE_CLASS(_name, _flags, _classOps) \
{ \
/* name */ \
_name, \
\
/* flags */ \
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | \
JSCLASS_IS_WRAPPED_NATIVE | JSCLASS_FOREGROUND_FINALIZE | \
(((_flags)&XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) \
? XPCONNECT_GLOBAL_FLAGS \
: 0), \
\
/* cOps */ \
_classOps, \
\
/* spec */ \
nullptr, \
\
/* ext */ \
&XPC_WN_JSClassExtension, \
\
/* oOps */ \
nullptr, \
#define XPC_MAKE_CLASS(_name, _flags, _classOps) \
{ \
/* name */ \
_name, \
\
/* flags */ \
JSCLASS_SLOT0_IS_NSISUPPORTS | JSCLASS_IS_WRAPPED_NATIVE | \
JSCLASS_FOREGROUND_FINALIZE | \
(((_flags)&XPC_SCRIPTABLE_IS_GLOBAL_OBJECT) \
? XPCONNECT_GLOBAL_FLAGS \
: JSCLASS_HAS_RESERVED_SLOTS(1)), \
\
/* cOps */ \
_classOps, \
\
/* spec */ \
nullptr, \
\
/* ext */ \
&XPC_WN_JSClassExtension, \
\
/* oOps */ \
nullptr, \
}
#endif

Просмотреть файл

@ -36,13 +36,14 @@ class SandboxPrivate : public nsIGlobalObject,
// The type used to cast to void needs to match the one in GetPrivate.
nsIScriptObjectPrincipal* sop =
static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take());
JS::SetPrivate(global, sop);
JS::SetObjectISupports(global, sop);
}
static SandboxPrivate* GetPrivate(JSObject* obj) {
// The type used to cast to void needs to match the one in Create.
return static_cast<SandboxPrivate*>(
static_cast<nsIScriptObjectPrincipal*>(JS::GetPrivate(obj)));
nsIScriptObjectPrincipal* sop =
JS::GetObjectISupports<nsIScriptObjectPrincipal>(obj);
return static_cast<SandboxPrivate*>(sop);
}
nsIPrincipal* GetPrincipal() override { return mPrincipal; }

Просмотреть файл

@ -55,9 +55,8 @@ using namespace JS;
bool XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface) {
const JSClass* jsclass = JS::GetClass(obj);
MOZ_ASSERT(jsclass, "obj has no class");
if (jsclass && (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
(jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
*iface = (nsISupports*)xpc_GetJSPrivate(obj);
if (jsclass && jsclass->slot0IsISupports()) {
*iface = JS::GetObjectISupports<nsISupports>(obj);
return true;
}
*iface = UnwrapDOMObjectToISupports(obj);

Просмотреть файл

@ -224,9 +224,7 @@ RealmPrivate::RealmPrivate(JS::Realm* realm) : scriptability(realm) {
void RealmPrivate::Init(HandleObject aGlobal, const SiteIdentifier& aSite) {
MOZ_ASSERT(aGlobal);
DebugOnly<const JSClass*> clasp = JS::GetClass(aGlobal);
MOZ_ASSERT(clasp->flags &
(JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE) ||
dom::IsDOMClass(clasp));
MOZ_ASSERT(clasp->slot0IsISupports() || dom::IsDOMClass(clasp));
Realm* realm = GetObjectRealmOrNull(aGlobal);

Просмотреть файл

@ -227,8 +227,10 @@ nsresult XPCWrappedNative::WrapNewGlobal(JSContext* cx,
// Set the JS object to the global we already created.
wrapper->SetFlatJSObject(global);
// Set the private to the XPCWrappedNative.
JS::SetPrivate(global, wrapper);
// Set the reserved slot to the XPCWrappedNative.
static_assert(JSCLASS_GLOBAL_APPLICATION_SLOTS > 0,
"Need at least one slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
JS::SetObjectISupports(global, wrapper);
// There are dire comments elsewhere in the code about how a GC can
// happen somewhere after wrapper initialization but before the wrapper is
@ -651,7 +653,7 @@ bool XPCWrappedNative::Init(JSContext* cx, nsIXPCScriptable* aScriptable) {
SetFlatJSObject(object);
JS::SetPrivate(mFlatJSObject, this);
JS::SetObjectISupports(mFlatJSObject, this);
return FinishInit(cx);
}
@ -809,7 +811,7 @@ void XPCWrappedNative::SystemIsBeingShutDown() {
// We leak mIdentity (see above).
// Short circuit future finalization.
JS::SetPrivate(mFlatJSObject, nullptr);
JS::SetObjectISupports(mFlatJSObject, nullptr);
UnsetFlatJSObject();
XPCWrappedNativeProto* proto = GetProto();

Просмотреть файл

@ -568,12 +568,11 @@ static void WrappedNativeFinalize(JSFreeOp* fop, JSObject* obj,
if (clazz->flags & JSCLASS_DOM_GLOBAL) {
mozilla::dom::DestroyProtoAndIfaceCache(obj);
}
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
if (!p) {
XPCWrappedNative* wrapper = JS::GetObjectISupports<XPCWrappedNative>(obj);
if (!wrapper) {
return;
}
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
if (helperType == WN_HELPER) {
wrapper->GetScriptable()->Finalize(wrapper, fop, obj);
}
@ -581,12 +580,11 @@ static void WrappedNativeFinalize(JSFreeOp* fop, JSObject* obj,
}
static size_t WrappedNativeObjectMoved(JSObject* obj, JSObject* old) {
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
if (!p) {
XPCWrappedNative* wrapper = JS::GetObjectISupports<XPCWrappedNative>(obj);
if (!wrapper) {
return 0;
}
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
wrapper->FlatJSObjectMoved(obj, old);
return 0;
}
@ -663,8 +661,8 @@ const js::ClassExtension XPC_WN_JSClassExtension = {
const JSClass XPC_WN_NoHelper_JSClass = {
"XPCWrappedNative_NoHelper",
JSCLASS_IS_WRAPPED_NATIVE | JSCLASS_HAS_PRIVATE |
JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_FOREGROUND_FINALIZE,
JSCLASS_IS_WRAPPED_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
JSCLASS_SLOT0_IS_NSISUPPORTS | JSCLASS_FOREGROUND_FINALIZE,
&XPC_WN_NoHelper_JSClassOps,
JS_NULL_CLASS_SPEC,
&XPC_WN_JSClassExtension,

Просмотреть файл

@ -1409,7 +1409,7 @@ class XPCWrappedNative final : public nsIXPConnectWrappedNative {
static XPCWrappedNative* Get(JSObject* obj) {
MOZ_ASSERT(IS_WN_REFLECTOR(obj));
return (XPCWrappedNative*)JS::GetPrivate(obj);
return JS::GetObjectISupports<XPCWrappedNative>(obj);
}
private:

Просмотреть файл

@ -193,8 +193,11 @@ struct RuntimeStats;
} // namespace JS
#define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \
JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | \
static_assert(JSCLASS_GLOBAL_APPLICATION_SLOTS > 0,
"Need at least one slot for JSCLASS_SLOT0_IS_NSISUPPORTS");
#define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \
JSCLASS_DOM_GLOBAL | JSCLASS_SLOT0_IS_NSISUPPORTS | \
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n)
#define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET \

Просмотреть файл

@ -777,15 +777,14 @@ JSObject* TransplantObjectNukingXrayWaiver(JSContext* cx,
nsIGlobalObject* NativeGlobal(JSObject* obj) {
obj = JS::GetNonCCWObjectGlobal(obj);
// Every global needs to hold a native as its private or be a
// Every global needs to hold a native as its first reserved slot or be a
// WebIDL object with an nsISupports DOM object.
MOZ_ASSERT((JS::GetClass(obj)->flags &
(JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE)) ||
MOZ_ASSERT(JS::GetClass(obj)->slot0IsISupports() ||
dom::UnwrapDOMObjectToISupports(obj));
nsISupports* native = dom::UnwrapDOMObjectToISupports(obj);
if (!native) {
native = static_cast<nsISupports*>(JS::GetPrivate(obj));
native = JS::GetObjectISupports<nsISupports>(obj);
MOZ_ASSERT(native);
// In some cases (like for windows) it is a wrapped native,

Просмотреть файл

@ -821,12 +821,11 @@ void CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(
return;
}
// XXX This test does seem fragile, we should probably whitelist classes
// XXX This test does seem fragile, we should probably allowlist classes
// that do hold a strong reference, but that might not be possible.
if (aClasp->flags & JSCLASS_HAS_PRIVATE &&
aClasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "JS::GetPrivate(obj)");
aCb.NoteXPCOMChild(static_cast<nsISupports*>(JS::GetPrivate(obj)));
if (aClasp->slot0IsISupports()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "JS::GetObjectISupports(obj)");
aCb.NoteXPCOMChild(JS::GetObjectISupports<nsISupports>(obj));
return;
}