Bug 1237504 - Refactor proxy slot layout to allow proxies to have more than 2 slots. r=bz,jonco

The patch makes the following proxy changes:

* The number of slots in ProxyValueArray is now dynamic and depends on the number of reserved slots we get from the Class.
* "Extra slots" was renamed to "Reserved slots" to make this clearer.
* All proxy Classes now have 2 reserved slots, but it should be easy to change that for proxy Classes that need more than 2 slots.
* Proxies now store a pointer to these slots and this means GetReservedSlot and SetReservedSlot can be used on proxies as well. We no longer need GetReservedOrProxyPrivateSlot and SetReservedOrProxyPrivateSlot.

And some changes to make DOM Proxies work with this:

* We now store the C++ object in the first reserved slot (DOM_OBJECT_SLOT) instead of in the proxy's private slot. This is pretty nice because it matches what we do for non-proxy DOM objects.
* We now store the expando in the proxy's private slot so I removed GetDOMProxyExpandoSlot and changed the IC code to get the expando from the private slot instead.
This commit is contained in:
Jan de Mooij 2017-04-28 14:12:28 +02:00
parent 37250338b7
commit f480e9ecdf
27 changed files with 337 additions and 260 deletions

View File

@ -1116,7 +1116,7 @@ protected:
static nsGlobalWindow* GetOuterWindow(JSObject *proxy)
{
nsGlobalWindow* outerWindow = nsGlobalWindow::FromSupports(
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
static_cast<nsISupports*>(js::GetProxyReservedSlot(proxy, 0).toPrivate()));
MOZ_ASSERT_IF(outerWindow, outerWindow->IsOuterWindow());
return outerWindow;
}
@ -1730,7 +1730,7 @@ nsGlobalWindow::~nsGlobalWindow()
if (IsOuterWindow()) {
JSObject *proxy = GetWrapperMaybeDead();
if (proxy) {
js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
js::SetProxyReservedSlot(proxy, 0, js::PrivateValue(nullptr));
}
// An outer window is destroyed with inner windows still possibly
@ -3144,7 +3144,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
js::SetProxyReservedSlot(outer, 0, js::PrivateValue(ToSupports(this)));
// Inform the nsJSContext, which is the canonical holder of the outer.
mContext->SetWindowProxy(outer);
@ -3162,8 +3162,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
js::SetProxyExtra(outerObject, 0, js::PrivateValue(nullptr));
js::SetProxyReservedSlot(obj, 0, js::PrivateValue(nullptr));
js::SetProxyReservedSlot(outerObject, 0, js::PrivateValue(nullptr));
outerObject = xpc::TransplantObject(cx, obj, outerObject);
if (!outerObject) {
@ -3171,7 +3171,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
return NS_ERROR_FAILURE;
}
js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
js::SetProxyReservedSlot(outerObject, 0, js::PrivateValue(ToSupports(this)));
SetWrapper(outerObject);

View File

@ -2205,9 +2205,9 @@ ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
// NB: It's important to do this _after_ copying the properties to
// propertyHolder. Otherwise, an object with |foo.x === foo| will
// crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
js::SetReservedOrProxyPrivateSlot(newobj, DOM_OBJECT_SLOT,
js::GetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT));
js::SetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
js::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
js::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
aObj = xpc::TransplantObject(aCx, aObj, newobj);
if (!aObj) {

View File

@ -135,7 +135,7 @@ UnwrapDOMObject(JSObject* obj)
MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
"Don't pass non-DOM objects to this function");
JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
return static_cast<T*>(val.toPrivate());
}
@ -150,7 +150,7 @@ UnwrapPossiblyNotInitializedDOMObject(JSObject* obj)
MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
"Don't pass non-DOM objects to this function");
JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
if (val.isUndefined()) {
return nullptr;
}
@ -2679,8 +2679,7 @@ public:
~BindingJSObjectCreator()
{
if (mReflector) {
js::SetReservedOrProxyPrivateSlot(mReflector, DOM_OBJECT_SLOT,
JS::UndefinedValue());
js::SetReservedSlot(mReflector, DOM_OBJECT_SLOT, JS::UndefinedValue());
}
}
@ -2688,14 +2687,15 @@ public:
CreateProxyObject(JSContext* aCx, const js::Class* aClass,
const DOMProxyHandler* aHandler,
JS::Handle<JSObject*> aProto, T* aNative,
JS::Handle<JS::Value> aExpandoValue,
JS::MutableHandle<JSObject*> aReflector)
{
js::ProxyOptions options;
options.setClass(aClass);
JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aNative));
aReflector.set(js::NewProxyObject(aCx, aHandler, proxyPrivateVal, aProto,
aReflector.set(js::NewProxyObject(aCx, aHandler, aExpandoValue, aProto,
options));
if (aReflector) {
js::SetProxyReservedSlot(aReflector, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
mNative = aNative;
mReflector = aReflector;
}

View File

@ -3499,21 +3499,21 @@ def CreateBindingJSObject(descriptor, properties):
# We don't always need to root obj, but there are a variety
# of cases where we do, so for simplicity, just always root it.
if descriptor.proxy:
create = dedent(
if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
expandoValue = "JS::PrivateValue(&aObject->mExpandoAndGeneration)"
else:
expandoValue = "JS::UndefinedValue()"
create = fill(
"""
JS::Rooted<JS::Value> expandoValue(aCx, ${expandoValue});
creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
proto, aObject, aReflector);
proto, aObject, expandoValue, aReflector);
if (!aReflector) {
return false;
}
""")
if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
create += dedent("""
js::SetProxyExtra(aReflector, JSPROXYSLOT_EXPANDO,
JS::PrivateValue(&aObject->mExpandoAndGeneration));
""")
""",
expandoValue=expandoValue)
else:
create = dedent(
"""
@ -11507,7 +11507,7 @@ class CGProxyUnwrap(CGAbstractMethod):
obj = js::UncheckedUnwrap(obj);
}
MOZ_ASSERT(IsProxy(obj));
return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate());
return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate());
""",
type=self.descriptor.nativeType)

View File

@ -34,7 +34,7 @@ js::DOMProxyShadowsResult
DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
{
JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(proxy);
bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
if (expando) {
bool hasOwn;
@ -64,7 +64,7 @@ struct SetDOMProxyInformation
{
SetDOMProxyInformation() {
js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
JSPROXYSLOT_EXPANDO, DOMProxyShadows);
DOMProxyShadows);
}
};
@ -75,7 +75,7 @@ void
DOMProxyHandler::ClearExternalRefsForWrapperRelease(JSObject* obj)
{
MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(obj);
if (v.isUndefined()) {
// No expando.
return;
@ -102,13 +102,13 @@ JSObject*
DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
{
MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(obj);
if (v.isUndefined()) {
return nullptr;
}
if (v.isObject()) {
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
js::SetProxyPrivate(obj, UndefinedValue());
xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
} else {
js::ExpandoAndGeneration* expandoAndGeneration =
@ -142,7 +142,7 @@ JSObject*
DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
{
NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(obj);
if (v.isObject()) {
return &v.toObject();
}
@ -178,7 +178,7 @@ DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
}
cache->SetPreservingWrapper(true);
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
js::SetProxyPrivate(obj, ObjectValue(*expando));
return expando;
}
@ -322,7 +322,7 @@ JSObject *
DOMProxyHandler::GetExpandoObject(JSObject *obj)
{
MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(obj);
if (v.isObject()) {
return &v.toObject();
}
@ -343,14 +343,12 @@ ShadowingDOMProxyHandler::trace(JSTracer* trc, JSObject* proxy) const
DOMProxyHandler::trace(trc, proxy);
MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
JS::Value v = js::GetProxyPrivate(proxy);
MOZ_ASSERT(!v.isObject(), "Should not have expando object directly!");
if (v.isUndefined()) {
// This can happen if we GC while creating our object, before we get a
// chance to set up its JSPROXYSLOT_EXPANDO slot.
return;
}
// The proxy's private slot is set when we allocate the proxy,
// so it cannot be |undefined|.
MOZ_ASSERT(!v.isUndefined());
js::ExpandoAndGeneration* expandoAndGeneration =
static_cast<js::ExpandoAndGeneration*>(v.toPrivate());

View File

@ -14,35 +14,29 @@
#include "js/Proxy.h"
#include "nsString.h"
#define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
namespace mozilla {
namespace dom {
enum {
/**
* DOM proxies have an extra slot for the expando object at index
* JSPROXYSLOT_EXPANDO.
*
* The expando object is a plain JSObject whose properties correspond to
* "expandos" (custom properties set by the script author).
*
* The exact value stored in the JSPROXYSLOT_EXPANDO slot depends on whether
* the interface is annotated with the [OverrideBuiltins] extended attribute.
*
* If it is, the proxy is initialized with a PrivateValue, which contains a
* pointer to a js::ExpandoAndGeneration object; this contains a pointer to
* the actual expando object as well as the "generation" of the object. The
* proxy handler will trace the expando object stored in the
* js::ExpandoAndGeneration while the proxy itself is alive.
*
* If it is not, the proxy is initialized with an UndefinedValue. In
* EnsureExpandoObject, it is set to an ObjectValue that points to the
* expando object directly. (It is set back to an UndefinedValue only when
* the object is about to die.)
*/
JSPROXYSLOT_EXPANDO = 0
};
/**
* DOM proxies store the expando object in the private slot.
*
* The expando object is a plain JSObject whose properties correspond to
* "expandos" (custom properties set by the script author).
*
* The exact value stored in the proxy's private slot depends on whether the
* interface is annotated with the [OverrideBuiltins] extended attribute.
*
* If it is, the proxy is initialized with a PrivateValue, which contains a
* pointer to a js::ExpandoAndGeneration object; this contains a pointer to
* the actual expando object as well as the "generation" of the object. The
* proxy handler will trace the expando object stored in the
* js::ExpandoAndGeneration while the proxy itself is alive.
*
* If it is not, the proxy is initialized with an UndefinedValue. In
* EnsureExpandoObject, it is set to an ObjectValue that points to the
* expando object directly. (It is set back to an UndefinedValue only when
* the object is about to die.)
*/
template<typename T> struct Prefable;

View File

@ -59,14 +59,14 @@ static inline AuxCPOWData*
AuxCPOWDataOf(JSObject* obj)
{
MOZ_ASSERT(IsCPOW(obj));
return static_cast<AuxCPOWData*>(GetProxyExtra(obj, 1).toPrivate());
return static_cast<AuxCPOWData*>(GetProxyReservedSlot(obj, 1).toPrivate());
}
static inline WrapperOwner*
OwnerOf(JSObject* obj)
{
MOZ_ASSERT(IsCPOW(obj));
return reinterpret_cast<WrapperOwner*>(GetProxyExtra(obj, 0).toPrivate());
return reinterpret_cast<WrapperOwner*>(GetProxyReservedSlot(obj, 0).toPrivate());
}
ObjectId
@ -1218,8 +1218,8 @@ WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar)
objVar.isDOMObject(),
objVar.objectTag());
SetProxyExtra(obj, 0, PrivateValue(this));
SetProxyExtra(obj, 1, PrivateValue(aux));
SetProxyReservedSlot(obj, 0, PrivateValue(this));
SetProxyReservedSlot(obj, 1, PrivateValue(aux));
}
if (!JS_WrapObject(cx, &obj))

View File

@ -367,30 +367,71 @@ inline bool IsProxy(const JSObject* obj)
}
namespace detail {
const uint32_t PROXY_EXTRA_SLOTS = 2;
// Layout of the values stored by a proxy. Note that API clients require the
// private slot to be the first slot in the proxy's values, so that the private
// slot can be accessed in the same fashion as the first reserved slot, via
// {Get,Set}ReservedOrProxyPrivateSlot.
// Proxy slot layout
// -----------------
//
// Every proxy has a ProxyValueArray that contains the following Values:
//
// - The private slot.
// - The reserved slots. The number of slots is determined by the proxy's Class.
//
// Proxy objects store a pointer to the reserved slots (ProxyReservedSlots*).
// The ProxyValueArray and the private slot can be accessed using
// ProxyValueArray::fromReservedSlots or ProxyDataLayout::values.
//
// Storing a pointer to ProxyReservedSlots instead of ProxyValueArray has a
// number of advantages. In particular, it means js::GetReservedSlot and
// js::SetReservedSlot can be used with both proxies and native objects. This
// works because the ProxyReservedSlots* pointer is stored where native objects
// store their dynamic slots pointer.
struct ProxyReservedSlots
{
Value slots[1];
static inline int offsetOfPrivateSlot();
void init(size_t nreserved) {
for (size_t i = 0; i < nreserved; i++)
slots[i] = JS::UndefinedValue();
}
ProxyReservedSlots(const ProxyReservedSlots&) = delete;
void operator=(const ProxyReservedSlots&) = delete;
};
struct ProxyValueArray
{
Value privateSlot;
Value extraSlots[PROXY_EXTRA_SLOTS];
ProxyReservedSlots reservedSlots;
ProxyValueArray()
: privateSlot(JS::UndefinedValue())
{
for (size_t i = 0; i < PROXY_EXTRA_SLOTS; i++)
extraSlots[i] = JS::UndefinedValue();
void init(size_t nreserved) {
privateSlot = JS::UndefinedValue();
reservedSlots.init(nreserved);
}
static size_t offsetOfPrivateSlot() {
return offsetof(ProxyValueArray, privateSlot);
static size_t sizeOf(size_t nreserved) {
return offsetOfReservedSlots() + nreserved * sizeof(Value);
}
static MOZ_ALWAYS_INLINE ProxyValueArray* fromReservedSlots(ProxyReservedSlots* slots) {
uintptr_t p = reinterpret_cast<uintptr_t>(slots);
return reinterpret_cast<ProxyValueArray*>(p - offsetOfReservedSlots());
}
static size_t offsetOfReservedSlots() {
return offsetof(ProxyValueArray, reservedSlots);
}
ProxyValueArray(const ProxyValueArray&) = delete;
void operator=(const ProxyValueArray&) = delete;
};
/* static */ inline int
ProxyReservedSlots::offsetOfPrivateSlot()
{
return -int(ProxyValueArray::offsetOfReservedSlots()) + offsetof(ProxyValueArray, privateSlot);
}
// All proxies share the same data layout. Following the object's shape and
// type, the proxy has a ProxyDataLayout structure with a pointer to an array
// of values and the proxy's handler. This is designed both so that proxies can
@ -401,8 +442,12 @@ struct ProxyValueArray
// See GetReservedOrProxyPrivateSlot below.
struct ProxyDataLayout
{
ProxyValueArray* values;
ProxyReservedSlots* reservedSlots;
const BaseProxyHandler* handler;
MOZ_ALWAYS_INLINE ProxyValueArray* values() const {
return ProxyValueArray::fromReservedSlots(reservedSlots);
}
};
const uint32_t ProxyDataOffset = 2 * sizeof(void*);
@ -432,7 +477,7 @@ GetProxyHandler(const JSObject* obj)
inline const Value&
GetProxyPrivate(const JSObject* obj)
{
return detail::GetProxyDataLayout(obj)->values->privateSlot;
return detail::GetProxyDataLayout(obj)->values()->privateSlot;
}
inline JSObject*
@ -442,10 +487,10 @@ GetProxyTargetObject(JSObject* obj)
}
inline const Value&
GetProxyExtra(const JSObject* obj, size_t n)
GetProxyReservedSlot(const JSObject* obj, size_t n)
{
MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
return detail::GetProxyDataLayout(obj)->values->extraSlots[n];
MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n];
}
inline void
@ -458,10 +503,10 @@ JS_FRIEND_API(void)
SetValueInProxy(Value* slot, const Value& value);
inline void
SetProxyExtra(JSObject* obj, size_t n, const Value& extra)
SetProxyReservedSlot(JSObject* obj, size_t n, const Value& extra)
{
MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
Value* vp = &detail::GetProxyDataLayout(obj)->values->extraSlots[n];
MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
Value* vp = &detail::GetProxyDataLayout(obj)->reservedSlots->slots[n];
// Trigger a barrier before writing the slot.
if (vp->isGCThing() || extra.isGCThing())
@ -470,32 +515,24 @@ SetProxyExtra(JSObject* obj, size_t n, const Value& extra)
*vp = extra;
}
inline void
SetProxyPrivate(JSObject* obj, const Value& value)
{
Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot;
// Trigger a barrier before writing the slot.
if (vp->isGCThing() || value.isGCThing())
SetValueInProxy(vp, value);
else
*vp = value;
}
inline bool
IsScriptedProxy(const JSObject* obj)
{
return IsProxy(obj) && GetProxyHandler(obj)->isScripted();
}
inline const Value&
GetReservedOrProxyPrivateSlot(const JSObject* obj, size_t slot)
{
MOZ_ASSERT(slot == 0);
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj));
return reinterpret_cast<const shadow::Object*>(obj)->slotRef(slot);
}
inline void
SetReservedOrProxyPrivateSlot(JSObject* obj, size_t slot, const Value& value)
{
MOZ_ASSERT(slot == 0);
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)) || IsProxy(obj));
shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
if (sobj->slotRef(slot).isGCThing() || value.isGCThing())
SetReservedOrProxyPrivateSlotWithBarrier(obj, slot, value);
else
sobj->slotRef(slot) = value;
}
class MOZ_STACK_CLASS ProxyOptions {
protected:
/* protected constructor for subclass */

View File

@ -1826,8 +1826,8 @@ BaselineCacheIRCompiler::emitLoadDOMExpandoValueGuardGeneration()
if (!addFailurePath(&failure))
return false;
masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), scratch);
Address expandoAddr(scratch, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot()));
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), scratch);
Address expandoAddr(scratch, detail::ProxyReservedSlots::offsetOfPrivateSlot());
// Load the ExpandoAndGeneration* in the output scratch register and guard
// it matches the proxy's ExpandoAndGeneration.

View File

@ -803,7 +803,7 @@ GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objI
{
MOZ_ASSERT(IsCacheableDOMProxy(obj));
RootedValue expandoVal(cx_, GetProxyExtra(obj, GetDOMProxyExpandoSlot()));
RootedValue expandoVal(cx_, GetProxyPrivate(obj));
RootedObject expandoObj(cx_);
if (expandoVal.isObject()) {
expandoObj = &expandoVal.toObject();
@ -873,7 +873,7 @@ CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id,
{
MOZ_ASSERT(IsCacheableDOMProxy(obj));
Value expandoVal = GetProxyExtra(obj, GetDOMProxyExpandoSlot());
Value expandoVal = GetProxyPrivate(obj);
ValOperandId expandoId;
if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
@ -3164,7 +3164,7 @@ SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objI
{
MOZ_ASSERT(IsCacheableDOMProxy(obj));
RootedValue expandoVal(cx_, GetProxyExtra(obj, GetDOMProxyExpandoSlot()));
RootedValue expandoVal(cx_, GetProxyPrivate(obj));
RootedObject expandoObj(cx_);
if (expandoVal.isObject()) {
expandoObj = &expandoVal.toObject();

View File

@ -1576,8 +1576,8 @@ CacheIRCompiler::emitLoadWrapperTarget()
Register obj = allocator.useRegister(masm, reader.objOperandId());
Register reg = allocator.defineRegister(masm, reader.objOperandId());
masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), reg);
masm.unboxObject(Address(reg, detail::ProxyValueArray::offsetOfPrivateSlot()), reg);
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), reg);
masm.unboxObject(Address(reg, detail::ProxyReservedSlots::offsetOfPrivateSlot()), reg);
return true;
}
@ -1587,9 +1587,9 @@ CacheIRCompiler::emitLoadDOMExpandoValue()
Register obj = allocator.useRegister(masm, reader.objOperandId());
ValueOperand val = allocator.defineValueRegister(masm, reader.valOperandId());
masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), val.scratchReg());
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), val.scratchReg());
masm.loadValue(Address(val.scratchReg(),
ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot())),
detail::ProxyReservedSlots::offsetOfPrivateSlot()),
val);
return true;
}
@ -1602,8 +1602,8 @@ CacheIRCompiler::emitLoadDOMExpandoValueIgnoreGeneration()
// Determine the expando's Address.
Register scratch = output.scratchReg();
masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), scratch);
Address expandoAddr(scratch, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot()));
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), scratch);
Address expandoAddr(scratch, detail::ProxyReservedSlots::offsetOfPrivateSlot());
#ifdef DEBUG
// Private values are stored as doubles, so assert we have a double.

View File

@ -1985,8 +1985,8 @@ IonCacheIRCompiler::emitLoadDOMExpandoValueGuardGeneration()
if (!addFailurePath(&failure))
return false;
masm.loadPtr(Address(obj, ProxyObject::offsetOfValues()), scratch1);
Address expandoAddr(scratch1, ProxyObject::offsetOfExtraSlotInValues(GetDOMProxyExpandoSlot()));
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), scratch1);
Address expandoAddr(scratch1, detail::ProxyReservedSlots::offsetOfPrivateSlot());
// Guard the ExpandoAndGeneration* matches the proxy's ExpandoAndGeneration.
masm.loadValue(expandoAddr, output);

View File

@ -74,15 +74,15 @@ BEGIN_TEST(testBug604087)
JS::RootedObject c2wrapper(cx, wrap(cx, outerObj, compartment2));
CHECK(c2wrapper);
c2wrapper->as<js::ProxyObject>().setExtra(0, js::Int32Value(2));
c2wrapper->as<js::ProxyObject>().setReservedSlot(0, js::Int32Value(2));
JS::RootedObject c3wrapper(cx, wrap(cx, outerObj, compartment3));
CHECK(c3wrapper);
c3wrapper->as<js::ProxyObject>().setExtra(0, js::Int32Value(3));
c3wrapper->as<js::ProxyObject>().setReservedSlot(0, js::Int32Value(3));
JS::RootedObject c4wrapper(cx, wrap(cx, outerObj, compartment4));
CHECK(c4wrapper);
c4wrapper->as<js::ProxyObject>().setExtra(0, js::Int32Value(4));
c4wrapper->as<js::ProxyObject>().setReservedSlot(0, js::Int32Value(4));
compartment4 = c4wrapper = nullptr;
JS::RootedObject next(cx);

View File

@ -546,14 +546,12 @@ js::GetOriginalEval(JSContext* cx, HandleObject scope, MutableHandleObject eval)
}
JS_FRIEND_API(void)
js::SetReservedOrProxyPrivateSlotWithBarrier(JSObject* obj, size_t slot, const js::Value& value)
js::SetReservedSlotWithBarrier(JSObject* obj, size_t slot, const js::Value& value)
{
if (IsProxy(obj)) {
MOZ_ASSERT(slot == 0);
obj->as<ProxyObject>().setSameCompartmentPrivate(value);
} else {
if (IsProxy(obj))
obj->as<ProxyObject>().setReservedSlot(slot, value);
else
obj->as<NativeObject>().setSlot(slot, value);
}
}
void
@ -1298,15 +1296,13 @@ js::GetDOMCallbacks(JSContext* cx)
}
static const void* gDOMProxyHandlerFamily = nullptr;
static uint32_t gDOMProxyExpandoSlot = 0;
static DOMProxyShadowsCheck gDOMProxyShadowsCheck;
JS_FRIEND_API(void)
js::SetDOMProxyInformation(const void* domProxyHandlerFamily, uint32_t domProxyExpandoSlot,
js::SetDOMProxyInformation(const void* domProxyHandlerFamily,
DOMProxyShadowsCheck domProxyShadowsCheck)
{
gDOMProxyHandlerFamily = domProxyHandlerFamily;
gDOMProxyExpandoSlot = domProxyExpandoSlot;
gDOMProxyShadowsCheck = domProxyShadowsCheck;
}
@ -1316,12 +1312,6 @@ js::GetDOMProxyHandlerFamily()
return gDOMProxyHandlerFamily;
}
uint32_t
js::GetDOMProxyExpandoSlot()
{
return gDOMProxyExpandoSlot;
}
DOMProxyShadowsCheck
js::GetDOMProxyShadowsCheck()
{

View File

@ -365,6 +365,7 @@ extern JS_FRIEND_DATA(const js::ObjectOps) ProxyObjectOps;
js::Class::NON_NATIVE | \
JSCLASS_IS_PROXY | \
JSCLASS_DELAY_METADATA_BUILDER | \
JSCLASS_HAS_RESERVED_SLOTS(2) | \
flags, \
&js::ProxyClassOps, \
JS_NULL_CLASS_SPEC, \
@ -722,6 +723,11 @@ GetObjectPrivate(JSObject* obj)
return *addr;
}
/**
* Get the value stored in an object's reserved slot. This can be used with
* both native objects and proxies, but if |obj| is known to be a proxy
* GetProxyReservedSlot is a bit more efficient.
*/
inline const JS::Value&
GetReservedSlot(JSObject* obj, size_t slot)
{
@ -730,15 +736,20 @@ GetReservedSlot(JSObject* obj, size_t slot)
}
JS_FRIEND_API(void)
SetReservedOrProxyPrivateSlotWithBarrier(JSObject* obj, size_t slot, const JS::Value& value);
SetReservedSlotWithBarrier(JSObject* obj, size_t slot, const JS::Value& value);
/**
* Store a value in an object's reserved slot. This can be used with
* both native objects and proxies, but if |obj| is known to be a proxy
* SetProxyReservedSlot is a bit more efficient.
*/
inline void
SetReservedSlot(JSObject* obj, size_t slot, const JS::Value& value)
{
MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
shadow::Object* sobj = reinterpret_cast<shadow::Object*>(obj);
if (sobj->slotRef(slot).isGCThing() || value.isGCThing())
SetReservedOrProxyPrivateSlotWithBarrier(obj, slot, value);
SetReservedSlotWithBarrier(obj, slot, value);
else
sobj->slotRef(slot) = value;
}
@ -1289,11 +1300,10 @@ typedef enum DOMProxyShadowsResult {
typedef DOMProxyShadowsResult
(* DOMProxyShadowsCheck)(JSContext* cx, JS::HandleObject object, JS::HandleId id);
JS_FRIEND_API(void)
SetDOMProxyInformation(const void* domProxyHandlerFamily, uint32_t domProxyExpandoSlot,
SetDOMProxyInformation(const void* domProxyHandlerFamily,
DOMProxyShadowsCheck domProxyShadowsCheck);
const void* GetDOMProxyHandlerFamily();
uint32_t GetDOMProxyExpandoSlot();
DOMProxyShadowsCheck GetDOMProxyShadowsCheck();
inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) {
return result == Shadows ||

View File

@ -4677,7 +4677,7 @@ IsGrayListObject(JSObject* obj)
}
/* static */ unsigned
ProxyObject::grayLinkExtraSlot(JSObject* obj)
ProxyObject::grayLinkReservedSlot(JSObject* obj)
{
MOZ_ASSERT(IsGrayListObject(obj));
return 1;
@ -4688,7 +4688,7 @@ static void
AssertNotOnGrayList(JSObject* obj)
{
MOZ_ASSERT_IF(IsGrayListObject(obj),
GetProxyExtra(obj, ProxyObject::grayLinkExtraSlot(obj)).isUndefined());
GetProxyReservedSlot(obj, ProxyObject::grayLinkReservedSlot(obj)).isUndefined());
}
#endif
@ -4716,12 +4716,12 @@ CrossCompartmentPointerReferent(JSObject* obj)
static JSObject*
NextIncomingCrossCompartmentPointer(JSObject* prev, bool unlink)
{
unsigned slot = ProxyObject::grayLinkExtraSlot(prev);
JSObject* next = GetProxyExtra(prev, slot).toObjectOrNull();
unsigned slot = ProxyObject::grayLinkReservedSlot(prev);
JSObject* next = GetProxyReservedSlot(prev, slot).toObjectOrNull();
MOZ_ASSERT_IF(next, IsGrayListObject(next));
if (unlink)
SetProxyExtra(prev, slot, UndefinedValue());
SetProxyReservedSlot(prev, slot, UndefinedValue());
return next;
}
@ -4732,15 +4732,15 @@ js::DelayCrossCompartmentGrayMarking(JSObject* src)
MOZ_ASSERT(IsGrayListObject(src));
/* Called from MarkCrossCompartmentXXX functions. */
unsigned slot = ProxyObject::grayLinkExtraSlot(src);
unsigned slot = ProxyObject::grayLinkReservedSlot(src);
JSObject* dest = CrossCompartmentPointerReferent(src);
JSCompartment* comp = dest->compartment();
if (GetProxyExtra(src, slot).isUndefined()) {
SetProxyExtra(src, slot, ObjectOrNullValue(comp->gcIncomingGrayPointers));
if (GetProxyReservedSlot(src, slot).isUndefined()) {
SetProxyReservedSlot(src, slot, ObjectOrNullValue(comp->gcIncomingGrayPointers));
comp->gcIncomingGrayPointers = src;
} else {
MOZ_ASSERT(GetProxyExtra(src, slot).isObjectOrNull());
MOZ_ASSERT(GetProxyReservedSlot(src, slot).isObjectOrNull());
}
#ifdef DEBUG
@ -4809,12 +4809,12 @@ RemoveFromGrayList(JSObject* wrapper)
if (!IsGrayListObject(wrapper))
return false;
unsigned slot = ProxyObject::grayLinkExtraSlot(wrapper);
if (GetProxyExtra(wrapper, slot).isUndefined())
unsigned slot = ProxyObject::grayLinkReservedSlot(wrapper);
if (GetProxyReservedSlot(wrapper, slot).isUndefined())
return false; /* Not on our list. */
JSObject* tail = GetProxyExtra(wrapper, slot).toObjectOrNull();
SetProxyExtra(wrapper, slot, UndefinedValue());
JSObject* tail = GetProxyReservedSlot(wrapper, slot).toObjectOrNull();
SetProxyReservedSlot(wrapper, slot, UndefinedValue());
JSCompartment* comp = CrossCompartmentPointerReferent(wrapper)->compartment();
JSObject* obj = comp->gcIncomingGrayPointers;
@ -4824,10 +4824,10 @@ RemoveFromGrayList(JSObject* wrapper)
}
while (obj) {
unsigned slot = ProxyObject::grayLinkExtraSlot(obj);
JSObject* next = GetProxyExtra(obj, slot).toObjectOrNull();
unsigned slot = ProxyObject::grayLinkReservedSlot(obj);
JSObject* next = GetProxyReservedSlot(obj, slot).toObjectOrNull();
if (next == wrapper) {
SetProxyExtra(obj, slot, ObjectOrNullValue(tail));
SetProxyReservedSlot(obj, slot, ObjectOrNullValue(tail));
return true;
}
obj = next;

View File

@ -1125,12 +1125,14 @@ CopyProxyObject(JSContext* cx, Handle<ProxyObject*> from, Handle<ProxyObject*> t
to->setSameCompartmentPrivate(v);
}
MOZ_ASSERT(from->numReservedSlots() == to->numReservedSlots());
RootedValue v(cx);
for (size_t n = 0; n < js::detail::PROXY_EXTRA_SLOTS; n++) {
v = GetProxyExtra(from, n);
for (size_t n = 0; n < from->numReservedSlots(); n++) {
v = GetProxyReservedSlot(from, n);
if (!cx->compartment()->wrap(cx, &v))
return false;
SetProxyExtra(to, n, v);
SetProxyReservedSlot(to, n, v);
}
return true;
@ -1528,13 +1530,58 @@ JSObject::fixDictionaryShapeAfterSwap()
as<NativeObject>().shape_->listp = &as<NativeObject>().shape_;
}
static void
RemoveFromStoreBuffer(JSContext* cx, js::detail::ProxyValueArray* values)
static MOZ_MUST_USE bool
CopyProxyValuesBeforeSwap(ProxyObject* proxy, Vector<Value>& values)
{
StoreBuffer& sb = cx->zone()->group()->storeBuffer();
sb.unputValue(&values->privateSlot);
for (size_t i = 0; i < js::detail::PROXY_EXTRA_SLOTS; i++)
sb.unputValue(&values->extraSlots[i]);
MOZ_ASSERT(values.empty());
// Remove the GCPtrValues we're about to swap from the store buffer, to
// ensure we don't trace bogus values.
StoreBuffer& sb = proxy->zone()->group()->storeBuffer();
// Reserve space for the private slot and the reserved slots.
if (!values.reserve(1 + proxy->numReservedSlots()))
return false;
js::detail::ProxyValueArray* valArray = js::detail::GetProxyDataLayout(proxy)->values();
sb.unputValue(&valArray->privateSlot);
values.infallibleAppend(valArray->privateSlot);
for (size_t i = 0; i < proxy->numReservedSlots(); i++) {
sb.unputValue(&valArray->reservedSlots.slots[i]);
values.infallibleAppend(valArray->reservedSlots.slots[i]);
}
return true;
}
bool
ProxyObject::initExternalValueArrayAfterSwap(JSContext* cx, const Vector<Value>& values)
{
MOZ_ASSERT(getClass()->isProxy());
size_t nreserved = numReservedSlots();
// |values| contains the private slot and the reserved slots.
MOZ_ASSERT(values.length() == 1 + nreserved);
size_t nbytes = js::detail::ProxyValueArray::sizeOf(nreserved);
auto* valArray =
reinterpret_cast<js::detail::ProxyValueArray*>(cx->zone()->pod_malloc<uint8_t>(nbytes));
if (!valArray)
return false;
valArray->privateSlot = values[0];
for (size_t i = 0; i < nreserved; i++)
valArray->reservedSlots.slots[i] = values[i + 1];
// Note: we allocate external slots iff the proxy had an inline
// ProxyValueArray, so at this point reservedSlots points into the
// old object and we don't have to free anything.
data.reservedSlots = &valArray->reservedSlots;
return true;
}
/* Use this method with extreme caution. It trades the guts of two objects. */
@ -1640,16 +1687,13 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b)
ProxyObject* proxyA = a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr;
ProxyObject* proxyB = b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr;
Maybe<js::detail::ProxyValueArray> proxyAVals, proxyBVals;
if (aIsProxyWithInlineValues) {
js::detail::ProxyValueArray* values = js::detail::GetProxyDataLayout(proxyA)->values;
proxyAVals.emplace(*values);
RemoveFromStoreBuffer(cx, values);
if (!CopyProxyValuesBeforeSwap(proxyA, avals))
oomUnsafe.crash("CopyProxyValuesBeforeSwap");
}
if (bIsProxyWithInlineValues) {
js::detail::ProxyValueArray* values = js::detail::GetProxyDataLayout(proxyB)->values;
proxyBVals.emplace(*values);
RemoveFromStoreBuffer(cx, values);
if (!CopyProxyValuesBeforeSwap(proxyB, bvals))
oomUnsafe.crash("CopyProxyValuesBeforeSwap");
}
// Swap the main fields of the objects, whether they are native objects or proxies.
@ -1670,11 +1714,11 @@ JSObject::swap(JSContext* cx, HandleObject a, HandleObject b)
oomUnsafe.crash("fillInAfterSwap");
}
if (aIsProxyWithInlineValues) {
if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, proxyAVals.ref()))
if (!b->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, avals))
oomUnsafe.crash("initExternalValueArray");
}
if (bIsProxyWithInlineValues) {
if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, proxyBVals.ref()))
if (!a->as<ProxyObject>().initExternalValueArrayAfterSwap(cx, bvals))
oomUnsafe.crash("initExternalValueArray");
}
}
@ -4078,11 +4122,11 @@ JSObject::debugCheckNewObject(ObjectGroup* group, Shape* shape, js::gc::AllocKin
MOZ_ASSERT(!group->compartment()->hasObjectPendingMetadata());
// Non-native classes cannot have reserved slots or private data, and the
// objects can't have any fixed slots, for compatibility with
// GetReservedOrProxyPrivateSlot.
// Non-native classes manage their own data and slots, so numFixedSlots and
// slotSpan are always 0. Note that proxy classes can have reserved slots
// but they're also not included in numFixedSlots/slotSpan.
if (!clasp->isNative()) {
MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == 0);
MOZ_ASSERT_IF(!clasp->isProxy(), JSCLASS_RESERVED_SLOTS(clasp) == 0);
MOZ_ASSERT(!clasp->hasPrivate());
MOZ_ASSERT_IF(shape, shape->numFixedSlots() == 0);
MOZ_ASSERT_IF(shape, shape->slotSpan() == 0);

View File

@ -685,14 +685,17 @@ ProxyObject::trace(JSTracer* trc, JSObject* obj)
// Note: If you add new slots here, make sure to change
// nuke() to cope.
TraceCrossCompartmentEdge(trc, obj, proxy->slotOfPrivate(), "private");
TraceEdge(trc, proxy->slotOfExtra(0), "extra0");
/*
* The GC can use the second reserved slot to link the cross compartment
* wrappers into a linked list, in which case we don't want to trace it.
*/
if (!proxy->is<CrossCompartmentWrapperObject>())
TraceEdge(trc, proxy->slotOfExtra(1), "extra1");
size_t nreserved = proxy->numReservedSlots();
for (size_t i = 0; i < nreserved; i++) {
/*
* The GC can use the second reserved slot to link the cross compartment
* wrappers into a linked list, in which case we don't want to trace it.
*/
if (proxy->is<CrossCompartmentWrapperObject>() && i == 1)
continue;
TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved");
}
Proxy::trace(trc, obj);
}
@ -714,7 +717,7 @@ proxy_Finalize(FreeOp* fop, JSObject* obj)
obj->as<ProxyObject>().handler()->finalize(fop, obj);
if (!obj->as<ProxyObject>().usingInlineValueArray())
js_free(js::detail::GetProxyDataLayout(obj)->values);
js_free(js::detail::GetProxyDataLayout(obj)->values());
}
static void
@ -803,8 +806,8 @@ ProxyObject::renew(const BaseProxyHandler* handler, const Value& priv)
setHandler(handler);
setCrossCompartmentPrivate(priv);
setExtra(0, UndefinedValue());
setExtra(1, UndefinedValue());
for (size_t i = 0; i < numReservedSlots(); i++)
setReservedSlot(i, UndefinedValue());
}
JS_FRIEND_API(JSObject*)

View File

@ -130,7 +130,7 @@ IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDe
ScriptedProxyHandler::handlerObject(const JSObject* proxy)
{
MOZ_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
return proxy->as<ProxyObject>().extra(ScriptedProxyHandler::HANDLER_EXTRA).toObjectOrNull();
return proxy->as<ProxyObject>().reservedSlot(ScriptedProxyHandler::HANDLER_EXTRA).toObjectOrNull();
}
// ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.9 GetMethod,
@ -1287,7 +1287,7 @@ bool
ScriptedProxyHandler::isCallable(JSObject* obj) const
{
MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
uint32_t callConstruct = obj->as<ProxyObject>().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
return !!(callConstruct & IS_CALLABLE);
}
@ -1295,7 +1295,7 @@ bool
ScriptedProxyHandler::isConstructor(JSObject* obj) const
{
MOZ_ASSERT(obj->as<ProxyObject>().handler() == &ScriptedProxyHandler::singleton);
uint32_t callConstruct = obj->as<ProxyObject>().extra(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
uint32_t callConstruct = obj->as<ProxyObject>().reservedSlot(IS_CALLCONSTRUCT_EXTRA).toPrivateUint32();
return !!(callConstruct & IS_CONSTRUCTOR);
}
@ -1350,13 +1350,13 @@ ProxyCreate(JSContext* cx, CallArgs& args, const char* callerName)
// Step 9 (reordered).
Rooted<ProxyObject*> proxy(cx, &proxy_->as<ProxyObject>());
proxy->setExtra(ScriptedProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
proxy->setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, ObjectValue(*handler));
// Step 7.
uint32_t callable = target->isCallable() ? ScriptedProxyHandler::IS_CALLABLE : 0;
uint32_t constructor = target->isConstructor() ? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
proxy->setExtra(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
PrivateUint32Value(callable | constructor));
proxy->setReservedSlot(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
PrivateUint32Value(callable | constructor));
// Step 10.
args.rval().setObject(*proxy);
@ -1388,7 +1388,7 @@ RevokeProxy(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT(p->is<ProxyObject>());
p->as<ProxyObject>().setSameCompartmentPrivate(NullValue());
p->as<ProxyObject>().setExtra(ScriptedProxyHandler::HANDLER_EXTRA, NullValue());
p->as<ProxyObject>().setReservedSlot(ScriptedProxyHandler::HANDLER_EXTRA, NullValue());
}
args.rval().setUndefined();

View File

@ -2268,8 +2268,8 @@ DebugEnvironmentProxy::create(JSContext* cx, EnvironmentObject& env, HandleObjec
return nullptr;
DebugEnvironmentProxy* debugEnv = &obj->as<DebugEnvironmentProxy>();
debugEnv->setExtra(ENCLOSING_EXTRA, ObjectValue(*enclosing));
debugEnv->setExtra(SNAPSHOT_EXTRA, NullValue());
debugEnv->setReservedSlot(ENCLOSING_SLOT, ObjectValue(*enclosing));
debugEnv->setReservedSlot(SNAPSHOT_SLOT, NullValue());
return debugEnv;
}
@ -2283,13 +2283,13 @@ DebugEnvironmentProxy::environment() const
JSObject&
DebugEnvironmentProxy::enclosingEnvironment() const
{
return extra(ENCLOSING_EXTRA).toObject();
return reservedSlot(ENCLOSING_SLOT).toObject();
}
ArrayObject*
DebugEnvironmentProxy::maybeSnapshot() const
{
JSObject* obj = extra(SNAPSHOT_EXTRA).toObjectOrNull();
JSObject* obj = reservedSlot(SNAPSHOT_SLOT).toObjectOrNull();
return obj ? &obj->as<ArrayObject>() : nullptr;
}
@ -2297,7 +2297,7 @@ void
DebugEnvironmentProxy::initSnapshot(ArrayObject& o)
{
MOZ_ASSERT(maybeSnapshot() == nullptr);
setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
setReservedSlot(SNAPSHOT_SLOT, ObjectValue(o));
}
bool

View File

@ -872,13 +872,13 @@ class DebugEnvironmentProxy : public ProxyObject
* The enclosing environment on the dynamic environment chain. This slot is analogous
* to the ENCLOSING_ENV_SLOT of a EnvironmentObject.
*/
static const unsigned ENCLOSING_EXTRA = 0;
static const unsigned ENCLOSING_SLOT = 0;
/*
* NullValue or a dense array holding the unaliased variables of a function
* frame that has been popped.
*/
static const unsigned SNAPSHOT_EXTRA = 1;
static const unsigned SNAPSHOT_SLOT = 1;
public:
static DebugEnvironmentProxy* create(JSContext* cx, EnvironmentObject& env,

View File

@ -15,12 +15,18 @@
using namespace js;
static gc::AllocKind
GetProxyGCObjectKind(const BaseProxyHandler* handler, const Value& priv)
GetProxyGCObjectKind(const Class* clasp, const BaseProxyHandler* handler, const Value& priv)
{
static_assert(sizeof(js::detail::ProxyValueArray) % sizeof(js::HeapSlot) == 0,
"ProxyValueArray must be a multiple of HeapSlot");
MOZ_ASSERT(clasp->isProxy());
uint32_t nslots = sizeof(js::detail::ProxyValueArray) / sizeof(HeapSlot);
uint32_t nreserved = JSCLASS_RESERVED_SLOTS(clasp);
MOZ_ASSERT(nreserved > 0);
MOZ_ASSERT(js::detail::ProxyValueArray::sizeOf(nreserved) % sizeof(Value) == 0,
"ProxyValueArray must be a multiple of Value");
uint32_t nslots = js::detail::ProxyValueArray::sizeOf(nreserved) / sizeof(Value);
MOZ_ASSERT(nslots <= NativeObject::MAX_FIXED_SLOTS);
gc::AllocKind kind = gc::GetGCObjectKind(nslots);
if (handler->finalizeInBackground(priv))
@ -68,7 +74,7 @@ ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue pri
newKind = TenuredObject;
}
gc::AllocKind allocKind = GetProxyGCObjectKind(handler, priv);
gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
AutoSetNewObjectMetadata metadata(cx);
// Note: this will initialize the object's |data| to strange values, but we
@ -77,7 +83,10 @@ ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue pri
JS_TRY_VAR_OR_RETURN_NULL(cx, proxy, create(cx, clasp, proto, allocKind, newKind));
proxy->setInlineValueArray();
new (proxy->data.values) detail::ProxyValueArray;
detail::ProxyValueArray* values = detail::GetProxyDataLayout(proxy)->values();
values->init(proxy->numReservedSlots());
proxy->data.handler = handler;
proxy->setCrossCompartmentPrivate(priv);
@ -93,7 +102,7 @@ ProxyObject::allocKindForTenure() const
{
MOZ_ASSERT(usingInlineValueArray());
Value priv = const_cast<ProxyObject*>(this)->private_();
return GetProxyGCObjectKind(data.handler, priv);
return GetProxyGCObjectKind(getClass(), data.handler, priv);
}
void
@ -133,7 +142,7 @@ ProxyObject::nuke()
setHandler(DeadObjectProxy<DeadProxyNotCallableNotConstructor>::singleton());
}
// The proxy's extra slots are not cleared and will continue to be
// 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
@ -190,19 +199,6 @@ ProxyObject::create(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto
return pobj;
}
bool
ProxyObject::initExternalValueArrayAfterSwap(JSContext* cx, const detail::ProxyValueArray& src)
{
MOZ_ASSERT(getClass()->isProxy());
auto* values = cx->zone()->new_<detail::ProxyValueArray>(src);
if (!values)
return false;
data.values = values;
return true;
}
JS_FRIEND_API(void)
js::SetValueInProxy(Value* slot, const Value& value)
{

View File

@ -29,6 +29,8 @@ class ProxyObject : public ShapedObject
"proxy object size must match GC thing size");
static_assert(offsetof(ProxyObject, data) == detail::ProxyDataOffset,
"proxy object layout must match shadow interface");
static_assert(offsetof(ProxyObject, data.reservedSlots) == offsetof(shadow::Object, slots),
"Proxy reservedSlots must overlay native object slots field");
}
static JS::Result<ProxyObject*, JS::OOM&>
@ -46,12 +48,12 @@ class ProxyObject : public ShapedObject
return (void*)(uintptr_t(this) + sizeof(ProxyObject));
}
bool usingInlineValueArray() const {
return data.values == inlineDataStart();
return data.values() == inlineDataStart();
}
void setInlineValueArray() {
data.values = reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart());
data.reservedSlots = &reinterpret_cast<detail::ProxyValueArray*>(inlineDataStart())->reservedSlots;
}
MOZ_MUST_USE bool initExternalValueArrayAfterSwap(JSContext* cx, const detail::ProxyValueArray& src);
MOZ_MUST_USE bool initExternalValueArrayAfterSwap(JSContext* cx, const Vector<Value>& values);
const Value& private_() {
return GetProxyPrivate(this);
@ -61,7 +63,7 @@ class ProxyObject : public ShapedObject
void setSameCompartmentPrivate(const Value& priv);
GCPtrValue* slotOfPrivate() {
return reinterpret_cast<GCPtrValue*>(&detail::GetProxyDataLayout(this)->values->privateSlot);
return reinterpret_cast<GCPtrValue*>(&detail::GetProxyDataLayout(this)->values()->privateSlot);
}
JSObject* target() const {
@ -76,31 +78,29 @@ class ProxyObject : public ShapedObject
SetProxyHandler(this, handler);
}
static size_t offsetOfValues() {
return offsetof(ProxyObject, data.values);
static size_t offsetOfReservedSlots() {
return offsetof(ProxyObject, data.reservedSlots);
}
static size_t offsetOfHandler() {
return offsetof(ProxyObject, data.handler);
}
static size_t offsetOfExtraSlotInValues(size_t slot) {
MOZ_ASSERT(slot < detail::PROXY_EXTRA_SLOTS);
return offsetof(detail::ProxyValueArray, extraSlots) + slot * sizeof(Value);
size_t numReservedSlots() const {
return JSCLASS_RESERVED_SLOTS(getClass());
}
const Value& reservedSlot(size_t n) const {
return GetProxyReservedSlot(const_cast<ProxyObject*>(this), n);
}
const Value& extra(size_t n) const {
return GetProxyExtra(const_cast<ProxyObject*>(this), n);
}
void setExtra(size_t n, const Value& extra) {
SetProxyExtra(this, n, extra);
void setReservedSlot(size_t n, const Value& extra) {
SetProxyReservedSlot(this, n, extra);
}
gc::AllocKind allocKindForTenure() const;
private:
GCPtrValue* slotOfExtra(size_t n) {
MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
return reinterpret_cast<GCPtrValue*>(&detail::GetProxyDataLayout(this)->values->extraSlots[n]);
GCPtrValue* reservedSlotPtr(size_t n) {
return reinterpret_cast<GCPtrValue*>(&detail::GetProxyDataLayout(this)->reservedSlots->slots[n]);
}
static bool isValidProxyClass(const Class* clasp) {
@ -116,7 +116,7 @@ class ProxyObject : public ShapedObject
}
public:
static unsigned grayLinkExtraSlot(JSObject* obj);
static unsigned grayLinkReservedSlot(JSObject* obj);
void renew(const BaseProxyHandler* handler, const Value& priv);

View File

@ -114,7 +114,14 @@
* a single BaseShape.
*/
#define JSSLOT_FREE(clasp) JSCLASS_RESERVED_SLOTS(clasp)
MOZ_ALWAYS_INLINE size_t
JSSLOT_FREE(const js::Class* clasp)
{
// Proxy classes have reserved slots, but proxies manage their own slot
// layout.
MOZ_ASSERT(!clasp->isProxy());
return JSCLASS_RESERVED_SLOTS(clasp);
}
namespace js {
@ -919,7 +926,10 @@ class Shape : public gc::TenuredCell
uint32_t slotSpan(const Class* clasp) const {
MOZ_ASSERT(!inDictionary());
uint32_t free = JSSLOT_FREE(clasp);
// Proxy classes have reserved slots, but proxies manage their own slot
// layout. This means all non-native object shapes have nfixed == 0 and
// slotSpan == 0.
uint32_t free = clasp->isProxy() ? 0 : JSSLOT_FREE(clasp);
return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
}
@ -1324,11 +1334,6 @@ struct StackShape
uint32_t slot() const { MOZ_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
uint32_t maybeSlot() const { return slot_; }
uint32_t slotSpan() const {
uint32_t free = JSSLOT_FREE(base->clasp_);
return hasMissingSlot() ? free : (maybeSlot() + 1);
}
void setSlot(uint32_t slot) {
MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
slot_ = slot;

View File

@ -716,8 +716,8 @@ WrapCallable(JSContext* cx, HandleObject callable, HandleObject sandboxProtoProx
JSObject* obj = js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
priv, nullptr, options);
if (obj) {
js::SetProxyExtra(obj, SandboxCallableProxyHandler::SandboxProxySlot,
ObjectValue(*sandboxProtoProxy));
js::SetProxyReservedSlot(obj, SandboxCallableProxyHandler::SandboxProxySlot,
ObjectValue(*sandboxProtoProxy));
}
return obj;

View File

@ -1318,14 +1318,14 @@ bool CloneExpandoChain(JSContext* cx, JSObject* dstArg, JSObject* srcArg)
static JSObject*
GetHolder(JSObject* obj)
{
return &js::GetProxyExtra(obj, 0).toObject();
return &js::GetProxyReservedSlot(obj, 0).toObject();
}
JSObject*
XrayTraits::getHolder(JSObject* wrapper)
{
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
js::Value v = js::GetProxyExtra(wrapper, 0);
js::Value v = js::GetProxyReservedSlot(wrapper, 0);
return v.isObject() ? &v.toObject() : nullptr;
}
@ -1337,7 +1337,7 @@ XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper)
return holder;
holder = createHolder(cx, wrapper); // virtual trap.
if (holder)
js::SetProxyExtra(wrapper, 0, ObjectValue(*holder));
js::SetProxyReservedSlot(wrapper, 0, ObjectValue(*holder));
return holder;
}

View File

@ -588,7 +588,7 @@ public:
static inline JSObject* getSandboxProxy(JS::Handle<JSObject*> proxy)
{
return &js::GetProxyExtra(proxy, SandboxProxySlot).toObject();
return &js::GetProxyReservedSlot(proxy, SandboxProxySlot).toObject();
}
};