Loading js/public/Proxy.h +13 −7 Original line number Diff line number Diff line Loading @@ -402,7 +402,7 @@ namespace detail { struct ProxyReservedSlots { JS::Value slots[1]; static inline int offsetOfPrivateSlot(); static constexpr ptrdiff_t offsetOfPrivateSlot(); static inline int offsetOfSlot(size_t slot) { return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(JS::Value); Loading @@ -429,24 +429,30 @@ struct ProxyValueArray { reservedSlots.init(nreserved); } static size_t sizeOf(size_t nreserved) { return offsetOfReservedSlots() + nreserved * sizeof(JS::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() { static constexpr size_t offsetOfReservedSlots() { return offsetof(ProxyValueArray, reservedSlots); } static size_t allocCount(size_t nreserved) { static_assert(offsetOfReservedSlots() % sizeof(JS::Value) == 0); return offsetOfReservedSlots() / sizeof(JS::Value) + nreserved; } static size_t sizeOf(size_t nreserved) { return allocCount(nreserved) * sizeof(JS::Value); } ProxyValueArray(const ProxyValueArray&) = delete; void operator=(const ProxyValueArray&) = delete; }; /* static */ inline int ProxyReservedSlots::offsetOfPrivateSlot() { return -int(ProxyValueArray::offsetOfReservedSlots()) + /* static */ constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() { return -ptrdiff_t(ProxyValueArray::offsetOfReservedSlots()) + offsetof(ProxyValueArray, privateSlot); } Loading js/src/gc/GCEnum.h +2 −1 Original line number Diff line number Diff line Loading @@ -142,7 +142,8 @@ enum class GCAbortReason { _(ZoneAllocPolicy) \ _(SharedArrayRawBuffer) \ _(XDRBufferElements) \ _(GlobalObjectData) _(GlobalObjectData) \ _(ProxyExternalValueArray) #define JS_FOR_EACH_MEMORY_USE(_) \ JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \ Loading js/src/gc/ObjectKind-inl.h +5 −1 Original line number Diff line number Diff line Loading @@ -118,6 +118,10 @@ static inline size_t GetGCKindBytes(AllocKind thingKind) { return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value); } static inline bool CanUseBackgroundAllocKind(const JSClass* clasp) { return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE); } static inline bool CanChangeToBackgroundAllocKind(AllocKind kind, const JSClass* clasp) { // If a foreground alloc kind is specified but the class has no finalizer or a Loading @@ -133,7 +137,7 @@ static inline bool CanChangeToBackgroundAllocKind(AllocKind kind, return false; // This kind is already a background finalized kind. } return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE); return CanUseBackgroundAllocKind(clasp); } static inline AllocKind ForegroundToBackgroundAllocKind(AllocKind fgKind) { Loading js/src/gc/Scheduling.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -755,9 +755,6 @@ void MemoryTracker::untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use) { } void MemoryTracker::swapGCMemory(Cell* a, Cell* b, MemoryUse use) { MOZ_ASSERT(a->isTenured()); MOZ_ASSERT(b->isTenured()); Key<Cell> ka{a, use}; Key<Cell> kb{b, use}; Loading js/src/jsapi-tests/testObjectSwap.cpp +59 −26 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ struct ProxyConfig { struct ObjectConfig { const JSClass* clasp; bool isNative; bool isPrototype; bool nurseryAllocated; union { NativeConfig native; ProxyConfig proxy; Loading Loading @@ -61,17 +61,27 @@ static const uint32_t TestPropertyCounts[] = {0, 1, 2, 7, 8, 20}; static bool Verbose = false; class DummyProxyHandler final : public ForwardingProxyHandler { class TenuredProxyHandler final : public Wrapper { public: static const DummyProxyHandler singleton; static const char family; constexpr DummyProxyHandler() : ForwardingProxyHandler(&family) {} static const TenuredProxyHandler singleton; constexpr TenuredProxyHandler() : Wrapper(0) {} bool canNurseryAllocate() const override { return false; } }; const DummyProxyHandler DummyProxyHandler::singleton; const char DummyProxyHandler::family = 0; const TenuredProxyHandler TenuredProxyHandler::singleton; class NurseryProxyHandler final : public Wrapper { public: static const NurseryProxyHandler singleton; constexpr NurseryProxyHandler() : Wrapper(0) {} bool canNurseryAllocate() const override { return true; } }; const NurseryProxyHandler NurseryProxyHandler::singleton; BEGIN_TEST(testObjectSwap) { AutoLeaveZeal noZeal(cx); ObjectConfigVector objectConfigs = CreateObjectConfigs(); for (const ObjectConfig& config1 : objectConfigs) { Loading @@ -85,7 +95,8 @@ BEGIN_TEST(testObjectSwap) { CHECK(obj2); if (Verbose) { fprintf(stderr, "Swap %p and %p\n", obj1.get(), obj2.get()); fprintf(stderr, "Swap %p (%s) and %p (%s)\n", obj1.get(), GetLocation(obj1), obj2.get(), GetLocation(obj2)); } { Loading Loading @@ -121,6 +132,9 @@ ObjectConfigVector CreateObjectConfigs() { for (uint32_t propCount : TestPropertyCounts) { config.native.propCount = propCount; for (bool nurseryAllocated : {false, true}) { config.nurseryAllocated = nurseryAllocated; for (bool inDictionaryMode : {false, true}) { if (inDictionaryMode && propCount == 0) { continue; Loading @@ -131,6 +145,7 @@ ObjectConfigVector CreateObjectConfigs() { } } } } config.isNative = false; config.proxy = ProxyConfig{false}; Loading @@ -138,15 +153,23 @@ ObjectConfigVector CreateObjectConfigs() { for (const JSClass& jsClass : TestProxyClasses) { config.clasp = &jsClass; for (bool inlineValues : {false, true}) { for (bool nurseryAllocated : {false, true}) { config.nurseryAllocated = nurseryAllocated; for (bool inlineValues : {true, false}) { config.proxy.inlineValues = inlineValues; MOZ_RELEASE_ASSERT(configs.append(config)); } } } return configs; } const char* GetLocation(JSObject* obj) { return obj->isTenured() ? "tenured heap" : "nursery"; } // Counter used to give slots and property names unique values. uint32_t nextId = 0; Loading @@ -158,12 +181,14 @@ JSObject* CreateObject(const ObjectConfig& config, uint32_t* idOut) { JSObject* CreateNativeObject(const ObjectConfig& config) { MOZ_ASSERT(config.isNative); RootedNativeObject obj( cx, NewBuiltinClassInstance(cx, config.clasp, TenuredObject)); NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject; RootedNativeObject obj(cx, NewBuiltinClassInstance(cx, config.clasp, kind)); if (!obj) { return nullptr; } MOZ_RELEASE_ASSERT(IsInsideNursery(obj) == config.nurseryAllocated); for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(config.clasp); i++) { JS::SetReservedSlot(obj, i, Int32Value(nextId++)); } Loading Loading @@ -212,8 +237,14 @@ JSObject* CreateProxy(const ObjectConfig& config) { options.setClass(config.clasp); options.setLazyProto(true); RootedObject obj(cx, NewProxyObject(cx, &DummyProxyHandler::singleton, priv, nullptr, options)); const Wrapper* handler; if (config.nurseryAllocated) { handler = &NurseryProxyHandler::singleton; } else { handler = &TenuredProxyHandler::singleton; } RootedObject obj(cx, NewProxyObject(cx, handler, priv, nullptr, options)); if (!obj) { return nullptr; } Loading @@ -228,8 +259,10 @@ JSObject* CreateProxy(const ObjectConfig& config) { if (!config.proxy.inlineValues) { // To create a proxy with non-inline values we must swap the proxy with an // object with a different size. RootedObject dummy( cx, NewBuiltinClassInstance(cx, &TestDOMClasses[0], TenuredObject)); NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject; RootedObject dummy(cx, NewBuiltinClassInstance(cx, &TestDOMClasses[0], kind)); if (!dummy) { return nullptr; } Loading @@ -239,6 +272,7 @@ JSObject* CreateProxy(const ObjectConfig& config) { proxy = &dummy->as<ProxyObject>(); } MOZ_RELEASE_ASSERT(IsInsideNursery(proxy) == config.nurseryAllocated); MOZ_RELEASE_ASSERT(proxy->usingInlineValueArray() == config.proxy.inlineValues); Loading @@ -255,14 +289,13 @@ bool CheckObject(HandleObject obj, const ObjectConfig& config, uint32_t id) { fprintf(stderr, "Check %p is a %s object with %u reserved slots", obj.get(), config.isNative ? "native" : "proxy", reservedSlots); if (config.isNative) { fprintf(stderr, ", %u properties and %s in dictionary mode", fprintf(stderr, ", %u properties and %s in dictionary mode\n", config.native.propCount, config.native.inDictionaryMode ? "is" : "is not"); } else { fprintf(stderr, " with %s values", fprintf(stderr, " with %s values\n", config.proxy.inlineValues ? "inline" : "out-of-line"); } fprintf(stderr, "\n"); } if (!config.isNative) { Loading Loading
js/public/Proxy.h +13 −7 Original line number Diff line number Diff line Loading @@ -402,7 +402,7 @@ namespace detail { struct ProxyReservedSlots { JS::Value slots[1]; static inline int offsetOfPrivateSlot(); static constexpr ptrdiff_t offsetOfPrivateSlot(); static inline int offsetOfSlot(size_t slot) { return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(JS::Value); Loading @@ -429,24 +429,30 @@ struct ProxyValueArray { reservedSlots.init(nreserved); } static size_t sizeOf(size_t nreserved) { return offsetOfReservedSlots() + nreserved * sizeof(JS::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() { static constexpr size_t offsetOfReservedSlots() { return offsetof(ProxyValueArray, reservedSlots); } static size_t allocCount(size_t nreserved) { static_assert(offsetOfReservedSlots() % sizeof(JS::Value) == 0); return offsetOfReservedSlots() / sizeof(JS::Value) + nreserved; } static size_t sizeOf(size_t nreserved) { return allocCount(nreserved) * sizeof(JS::Value); } ProxyValueArray(const ProxyValueArray&) = delete; void operator=(const ProxyValueArray&) = delete; }; /* static */ inline int ProxyReservedSlots::offsetOfPrivateSlot() { return -int(ProxyValueArray::offsetOfReservedSlots()) + /* static */ constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() { return -ptrdiff_t(ProxyValueArray::offsetOfReservedSlots()) + offsetof(ProxyValueArray, privateSlot); } Loading
js/src/gc/GCEnum.h +2 −1 Original line number Diff line number Diff line Loading @@ -142,7 +142,8 @@ enum class GCAbortReason { _(ZoneAllocPolicy) \ _(SharedArrayRawBuffer) \ _(XDRBufferElements) \ _(GlobalObjectData) _(GlobalObjectData) \ _(ProxyExternalValueArray) #define JS_FOR_EACH_MEMORY_USE(_) \ JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \ Loading
js/src/gc/ObjectKind-inl.h +5 −1 Original line number Diff line number Diff line Loading @@ -118,6 +118,10 @@ static inline size_t GetGCKindBytes(AllocKind thingKind) { return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value); } static inline bool CanUseBackgroundAllocKind(const JSClass* clasp) { return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE); } static inline bool CanChangeToBackgroundAllocKind(AllocKind kind, const JSClass* clasp) { // If a foreground alloc kind is specified but the class has no finalizer or a Loading @@ -133,7 +137,7 @@ static inline bool CanChangeToBackgroundAllocKind(AllocKind kind, return false; // This kind is already a background finalized kind. } return !clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE); return CanUseBackgroundAllocKind(clasp); } static inline AllocKind ForegroundToBackgroundAllocKind(AllocKind fgKind) { Loading
js/src/gc/Scheduling.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -755,9 +755,6 @@ void MemoryTracker::untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use) { } void MemoryTracker::swapGCMemory(Cell* a, Cell* b, MemoryUse use) { MOZ_ASSERT(a->isTenured()); MOZ_ASSERT(b->isTenured()); Key<Cell> ka{a, use}; Key<Cell> kb{b, use}; Loading
js/src/jsapi-tests/testObjectSwap.cpp +59 −26 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ struct ProxyConfig { struct ObjectConfig { const JSClass* clasp; bool isNative; bool isPrototype; bool nurseryAllocated; union { NativeConfig native; ProxyConfig proxy; Loading Loading @@ -61,17 +61,27 @@ static const uint32_t TestPropertyCounts[] = {0, 1, 2, 7, 8, 20}; static bool Verbose = false; class DummyProxyHandler final : public ForwardingProxyHandler { class TenuredProxyHandler final : public Wrapper { public: static const DummyProxyHandler singleton; static const char family; constexpr DummyProxyHandler() : ForwardingProxyHandler(&family) {} static const TenuredProxyHandler singleton; constexpr TenuredProxyHandler() : Wrapper(0) {} bool canNurseryAllocate() const override { return false; } }; const DummyProxyHandler DummyProxyHandler::singleton; const char DummyProxyHandler::family = 0; const TenuredProxyHandler TenuredProxyHandler::singleton; class NurseryProxyHandler final : public Wrapper { public: static const NurseryProxyHandler singleton; constexpr NurseryProxyHandler() : Wrapper(0) {} bool canNurseryAllocate() const override { return true; } }; const NurseryProxyHandler NurseryProxyHandler::singleton; BEGIN_TEST(testObjectSwap) { AutoLeaveZeal noZeal(cx); ObjectConfigVector objectConfigs = CreateObjectConfigs(); for (const ObjectConfig& config1 : objectConfigs) { Loading @@ -85,7 +95,8 @@ BEGIN_TEST(testObjectSwap) { CHECK(obj2); if (Verbose) { fprintf(stderr, "Swap %p and %p\n", obj1.get(), obj2.get()); fprintf(stderr, "Swap %p (%s) and %p (%s)\n", obj1.get(), GetLocation(obj1), obj2.get(), GetLocation(obj2)); } { Loading Loading @@ -121,6 +132,9 @@ ObjectConfigVector CreateObjectConfigs() { for (uint32_t propCount : TestPropertyCounts) { config.native.propCount = propCount; for (bool nurseryAllocated : {false, true}) { config.nurseryAllocated = nurseryAllocated; for (bool inDictionaryMode : {false, true}) { if (inDictionaryMode && propCount == 0) { continue; Loading @@ -131,6 +145,7 @@ ObjectConfigVector CreateObjectConfigs() { } } } } config.isNative = false; config.proxy = ProxyConfig{false}; Loading @@ -138,15 +153,23 @@ ObjectConfigVector CreateObjectConfigs() { for (const JSClass& jsClass : TestProxyClasses) { config.clasp = &jsClass; for (bool inlineValues : {false, true}) { for (bool nurseryAllocated : {false, true}) { config.nurseryAllocated = nurseryAllocated; for (bool inlineValues : {true, false}) { config.proxy.inlineValues = inlineValues; MOZ_RELEASE_ASSERT(configs.append(config)); } } } return configs; } const char* GetLocation(JSObject* obj) { return obj->isTenured() ? "tenured heap" : "nursery"; } // Counter used to give slots and property names unique values. uint32_t nextId = 0; Loading @@ -158,12 +181,14 @@ JSObject* CreateObject(const ObjectConfig& config, uint32_t* idOut) { JSObject* CreateNativeObject(const ObjectConfig& config) { MOZ_ASSERT(config.isNative); RootedNativeObject obj( cx, NewBuiltinClassInstance(cx, config.clasp, TenuredObject)); NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject; RootedNativeObject obj(cx, NewBuiltinClassInstance(cx, config.clasp, kind)); if (!obj) { return nullptr; } MOZ_RELEASE_ASSERT(IsInsideNursery(obj) == config.nurseryAllocated); for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(config.clasp); i++) { JS::SetReservedSlot(obj, i, Int32Value(nextId++)); } Loading Loading @@ -212,8 +237,14 @@ JSObject* CreateProxy(const ObjectConfig& config) { options.setClass(config.clasp); options.setLazyProto(true); RootedObject obj(cx, NewProxyObject(cx, &DummyProxyHandler::singleton, priv, nullptr, options)); const Wrapper* handler; if (config.nurseryAllocated) { handler = &NurseryProxyHandler::singleton; } else { handler = &TenuredProxyHandler::singleton; } RootedObject obj(cx, NewProxyObject(cx, handler, priv, nullptr, options)); if (!obj) { return nullptr; } Loading @@ -228,8 +259,10 @@ JSObject* CreateProxy(const ObjectConfig& config) { if (!config.proxy.inlineValues) { // To create a proxy with non-inline values we must swap the proxy with an // object with a different size. RootedObject dummy( cx, NewBuiltinClassInstance(cx, &TestDOMClasses[0], TenuredObject)); NewObjectKind kind = config.nurseryAllocated ? GenericObject : TenuredObject; RootedObject dummy(cx, NewBuiltinClassInstance(cx, &TestDOMClasses[0], kind)); if (!dummy) { return nullptr; } Loading @@ -239,6 +272,7 @@ JSObject* CreateProxy(const ObjectConfig& config) { proxy = &dummy->as<ProxyObject>(); } MOZ_RELEASE_ASSERT(IsInsideNursery(proxy) == config.nurseryAllocated); MOZ_RELEASE_ASSERT(proxy->usingInlineValueArray() == config.proxy.inlineValues); Loading @@ -255,14 +289,13 @@ bool CheckObject(HandleObject obj, const ObjectConfig& config, uint32_t id) { fprintf(stderr, "Check %p is a %s object with %u reserved slots", obj.get(), config.isNative ? "native" : "proxy", reservedSlots); if (config.isNative) { fprintf(stderr, ", %u properties and %s in dictionary mode", fprintf(stderr, ", %u properties and %s in dictionary mode\n", config.native.propCount, config.native.inDictionaryMode ? "is" : "is not"); } else { fprintf(stderr, " with %s values", fprintf(stderr, " with %s values\n", config.proxy.inlineValues ? "inline" : "out-of-line"); } fprintf(stderr, "\n"); } if (!config.isNative) { Loading