Bug 1143256 - Store object metadata using a weak map, r=luke.

This commit is contained in:
Brian Hackett 2015-03-25 09:07:51 -07:00
parent e3f8274ddc
commit 533750696e
39 changed files with 277 additions and 586 deletions

View File

@ -599,6 +599,7 @@ struct CompartmentStats
macro(Other, NotLiveGCThing, compartmentTables) \
macro(Other, NotLiveGCThing, innerViewsTable) \
macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \
macro(Other, NotLiveGCThing, objectMetadataTable) \
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
macro(Other, NotLiveGCThing, regexpCompartment) \
macro(Other, NotLiveGCThing, savedStacksSet)

View File

@ -1394,16 +1394,16 @@ DisplayName(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
static bool
ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
static JSObject *
ShellObjectMetadataCallback(JSContext *cx)
{
RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!obj)
return false;
CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
RootedObject stack(cx, NewDenseEmptyArray(cx));
if (!stack)
return false;
CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
static int createdIndex = 0;
createdIndex++;
@ -1411,13 +1411,13 @@ ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
JS_STUBGETTER, JS_STUBSETTER))
{
return false;
CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
}
if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
JS_STUBGETTER, JS_STUBSETTER))
{
return false;
CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
}
int stackIndex = 0;
@ -1430,14 +1430,13 @@ ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
if (!JS_DefinePropertyById(cx, stack, id, callee, 0,
JS_STUBGETTER, JS_STUBSETTER))
{
return false;
CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
}
stackIndex++;
}
}
*pmetadata = obj;
return true;
return obj;
}
static bool
@ -1452,22 +1451,6 @@ SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
static bool
SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 2 || !args[0].isObject() || !args[1].isObject()) {
JS_ReportError(cx, "Both arguments must be objects");
return false;
}
args.rval().setUndefined();
RootedObject obj(cx, &args[0].toObject());
RootedObject metadata(cx, &args[1].toObject());
return SetObjectMetadata(cx, obj, metadata);
}
static bool
GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
{
@ -2776,10 +2759,6 @@ gc::ZealModeHelpText),
"setObjectMetadataCallback(fn)",
" Specify function to supply metadata for all newly created objects."),
JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0,
"setObjectMetadata(obj, metadataObj)",
" Change the metadata for an object."),
JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
"getObjectMetadata(obj)",
" Get the metadata for an object."),

View File

@ -19,6 +19,7 @@
#include "vm/String.h"
#include "vm/StringBuffer.h"
#include "vm/TypedArrayObject.h"
#include "vm/WeakMapObject.h"
#include "jsatominlines.h"
#include "jsobjinlines.h"
@ -1378,12 +1379,11 @@ bool
TypedObject::isAttached() const
{
if (is<InlineTransparentTypedObject>()) {
LazyArrayBufferTable *table = compartment()->lazyArrayBuffers;
ObjectWeakMap *table = compartment()->lazyArrayBuffers;
if (table) {
ArrayBufferObject *buffer =
table->maybeBuffer(&const_cast<TypedObject *>(this)->as<InlineTransparentTypedObject>());
JSObject *buffer = table->lookup(this);
if (buffer)
return !buffer->isNeutered();
return !buffer->as<ArrayBufferObject>().isNeutered();
}
return true;
}
@ -2193,16 +2193,16 @@ InlineTypedObject::objectMovedDuringMinorGC(JSTracer *trc, JSObject *dst, JSObje
ArrayBufferObject *
InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
{
LazyArrayBufferTable *&table = cx->compartment()->lazyArrayBuffers;
ObjectWeakMap *&table = cx->compartment()->lazyArrayBuffers;
if (!table) {
table = cx->new_<LazyArrayBufferTable>(cx);
table = cx->new_<ObjectWeakMap>(cx);
if (!table)
return nullptr;
}
ArrayBufferObject *buffer = table->maybeBuffer(this);
if (buffer)
return buffer;
JSObject *obj = table->lookup(this);
if (obj)
return &obj->as<ArrayBufferObject>();
ArrayBufferObject::BufferContents contents =
ArrayBufferObject::BufferContents::createPlain(inlineTypedMem());
@ -2212,7 +2212,8 @@ InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
// and its contents.
gc::AutoSuppressGC suppress(cx);
buffer = ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
ArrayBufferObject *buffer =
ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData);
if (!buffer)
return nullptr;
@ -2226,9 +2227,15 @@ InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
buffer->setForInlineTypedObject();
buffer->setHasTypedObjectViews();
if (!table->addBuffer(cx, this, buffer))
if (!table->add(cx, this, buffer))
return nullptr;
if (IsInsideNursery(this)) {
// Make sure the buffer is traced by the next generational collection,
// so that its data pointer is updated after this typed object moves.
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(buffer);
}
return buffer;
}
@ -2240,67 +2247,6 @@ OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx)
return owner().as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
}
LazyArrayBufferTable::LazyArrayBufferTable(JSContext *cx)
: map(cx)
{
if (!map.init())
CrashAtUnhandlableOOM("LazyArrayBufferTable");
}
LazyArrayBufferTable::~LazyArrayBufferTable()
{
WeakMapBase::removeWeakMapFromList(&map);
}
ArrayBufferObject *
LazyArrayBufferTable::maybeBuffer(InlineTransparentTypedObject *obj)
{
if (Map::Ptr p = map.lookup(obj))
return &p->value()->as<ArrayBufferObject>();
return nullptr;
}
bool
LazyArrayBufferTable::addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer)
{
MOZ_ASSERT(!map.has(obj));
if (!map.put(obj, buffer)) {
ReportOutOfMemory(cx);
return false;
}
MOZ_ASSERT(!IsInsideNursery(buffer));
if (IsInsideNursery(obj)) {
// Strip the barriers from the type before inserting into the store
// buffer, as is done for DebugScopes::proxiedScopes.
Map::Base *baseHashMap = static_cast<Map::Base *>(&map);
typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
cx->runtime()->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, obj));
// Also make sure the buffer is traced, so that its data pointer is
// updated after the typed object moves.
cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(buffer);
}
return true;
}
void
LazyArrayBufferTable::trace(JSTracer *trc)
{
map.trace(trc);
}
size_t
LazyArrayBufferTable::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return mallocSizeOf(this) + map.sizeOfExcludingThis(mallocSizeOf);
}
/******************************************************************************
* Typed object classes
*/

View File

@ -1021,30 +1021,6 @@ TypedObject::opaque() const
return IsOpaqueTypedObjectClass(getClass());
}
// Inline transparent typed objects do not initially have an array buffer, but
// can have that buffer created lazily if it is accessed later. This table
// manages references from such typed objects to their buffers.
class LazyArrayBufferTable
{
private:
// The map from transparent typed objects to their lazily created buffer.
// Keys in this map are InlineTransparentTypedObjects and values are
// ArrayBufferObjects, but we don't enforce this in the type system due to
// the extra marking code goop that requires.
typedef WeakMap<PreBarrieredObject, RelocatablePtrObject> Map;
Map map;
public:
explicit LazyArrayBufferTable(JSContext *cx);
~LazyArrayBufferTable();
ArrayBufferObject *maybeBuffer(InlineTransparentTypedObject *obj);
bool addBuffer(JSContext *cx, InlineTransparentTypedObject *obj, ArrayBufferObject *buffer);
void trace(JSTracer *trc);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
};
JSObject *
InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj);

View File

@ -1073,9 +1073,6 @@ BaseShape::markChildren(JSTracer *trc)
JSObject* global = compartment()->unsafeUnbarrieredMaybeGlobal();
if (global)
MarkObjectUnbarriered(trc, &global, "global");
if (metadata)
gc::MarkObject(trc, &metadata, "metadata");
}
static void
@ -1122,9 +1119,6 @@ ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
if (GlobalObject *global = base->compartment()->unsafeUnbarrieredMaybeGlobal())
gcmarker->traverse(global);
if (JSObject *metadata = base->getObjectMetadata())
MaybePushMarkStackBetweenSlices(gcmarker, metadata);
/*
* All children of the owned base shape are consistent with its
* unowned one, thus we do not need to trace through children of the

View File

@ -307,7 +307,7 @@ Mark(JSTracer *trc, NativeObject **obj, const char *name)
MarkObjectUnbarriered(trc, obj, name);
}
/* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */
/* For use by Debugger::WeakMap's liveScopes HashKeyRef instantiation. */
inline void
Mark(JSTracer *trc, ScopeObject **obj, const char *name)
{

View File

@ -24,6 +24,7 @@
#include "js/HashTable.h"
#include "vm/Debugger.h"
#include "vm/JSONParser.h"
#include "vm/WeakMapObject.h"
#include "jsgcinlines.h"
#include "jsobjinlines.h"
@ -515,6 +516,9 @@ js::gc::GCRuntime::markRuntime(JSTracer *trc,
if (c->lazyArrayBuffers)
c->lazyArrayBuffers->trace(trc);
if (c->objectMetadataTable)
c->objectMetadataTable->trace(trc);
}
MarkInterpreterActivations(rt, trc);

View File

@ -1,8 +1,4 @@
x = [1,2,3];
setObjectMetadata(x, {y:0});
assertEq(getObjectMetadata(x).y, 0);
setObjectMetadataCallback(true);
function Foo() {

View File

@ -90,7 +90,12 @@ JSObject *
NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap,
size_t ndynamic, const js::Class *clasp)
{
return js::Allocate<JSObject>(cx, allocKind, ndynamic, initialHeap, clasp);
JSObject *obj = js::Allocate<JSObject>(cx, allocKind, ndynamic, initialHeap, clasp);
if (!obj)
return nullptr;
SetNewObjectMetadata(cx, obj);
return obj;
}
bool

View File

@ -3216,12 +3216,8 @@ CreateArrayPrototype(JSContext *cx, JSProtoKey key)
if (!group)
return nullptr;
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cx, &metadata))
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
metadata, gc::AllocKind::OBJECT0));
gc::AllocKind::OBJECT0));
if (!shape)
return nullptr;
@ -3294,9 +3290,7 @@ EnsureNewArrayElements(ExclusiveContext *cx, ArrayObject *obj, uint32_t length)
static bool
NewArrayIsCachable(ExclusiveContext *cxArg, NewObjectKind newKind)
{
return cxArg->isJSContext() &&
newKind == GenericObject &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback();
return cxArg->isJSContext() && newKind == GenericObject;
}
template <uint32_t maxLength>
@ -3341,17 +3335,13 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
if (!group)
return nullptr;
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cxArg, &metadata))
return nullptr;
/*
* Get a shape with zero fixed slots, regardless of the size class.
* See JSObject::createArray.
*/
RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
TaggedProto(proto),
metadata, gc::AllocKind::OBJECT0));
gc::AllocKind::OBJECT0));
if (!shape)
return nullptr;
@ -3518,20 +3508,9 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSOb
JSObject *
js::NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject templateObject, gc::InitialHeap heap)
{
RootedShape shape(cx, templateObject->lastProperty());
MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cx, &metadata))
return nullptr;
if (metadata) {
shape = Shape::setObjectMetadata(cx, metadata, templateObject->getTaggedProto(), shape);
if (!shape)
return nullptr;
}
ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, shape, templateObject);
ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, templateObject);
if (!arr)
return nullptr;

View File

@ -62,6 +62,7 @@ JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options =
neuteredTypedObjects(0),
propertyTree(thisForCtor()),
selfHostingScriptSource(nullptr),
objectMetadataTable(nullptr),
lazyArrayBuffers(nullptr),
gcIncomingGrayPointers(nullptr),
gcWeakMapList(nullptr),
@ -92,6 +93,7 @@ JSCompartment::~JSCompartment()
js_delete(scriptCountsMap);
js_delete(debugScriptMap);
js_delete(debugScopes);
js_delete(objectMetadataTable);
js_delete(lazyArrayBuffers);
js_free(enumerators);
@ -647,7 +649,6 @@ void JSCompartment::fixupAfterMovingGC()
{
fixupGlobal();
fixupInitialShapeTable();
fixupBaseShapeTable();
objectGroups.fixupTablesAfterMovingGC();
}
@ -699,6 +700,29 @@ JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback)
objectMetadataCallback = callback;
}
void
JSCompartment::clearObjectMetadata()
{
js_delete(objectMetadataTable);
objectMetadataTable = nullptr;
}
void
JSCompartment::setNewObjectMetadata(JSContext *cx, JSObject *obj)
{
MOZ_ASSERT(this == cx->compartment());
if (JSObject *metadata = objectMetadataCallback(cx)) {
if (!objectMetadataTable) {
objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
if (!objectMetadataTable)
CrashAtUnhandlableOOM("setObjectMetadata");
}
if (!objectMetadataTable->add(cx, obj, metadata))
CrashAtUnhandlableOOM("setObjectMetadata");
}
}
static bool
AddInnerLazyFunctionsFromScript(JSScript *script, AutoObjectVector &lazyFunctions)
{
@ -821,6 +845,7 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t *compartmentTables,
size_t *innerViewsArg,
size_t *lazyArrayBuffersArg,
size_t *objectMetadataTablesArg,
size_t *crossCompartmentWrappersArg,
size_t *regexpCompartment,
size_t *savedStacksSet)
@ -834,6 +859,8 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
*innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf);
if (lazyArrayBuffers)
*lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf);
if (objectMetadataTable)
*objectMetadataTablesArg += objectMetadataTable->sizeOfIncludingThis(mallocSizeOf);
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
*regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
*savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);

View File

@ -131,7 +131,7 @@ struct TypeInferenceSizes;
namespace js {
class DebugScopes;
class LazyArrayBufferTable;
class ObjectWeakMap;
class WeakMapBase;
}
@ -257,6 +257,7 @@ struct JSCompartment
size_t *compartmentTables,
size_t *innerViews,
size_t *lazyArrayBuffers,
size_t *objectMetadataTables,
size_t *crossCompartmentWrappers,
size_t *regexpCompartment,
size_t *savedStacksSet);
@ -289,11 +290,17 @@ struct JSCompartment
*/
js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
// Keep track of the metadata objects which can be associated with each
// JS object.
js::ObjectWeakMap *objectMetadataTable;
// Map from array buffers to views sharing that storage.
js::InnerViewTable innerViews;
// Map from typed objects to array buffers lazily created for them.
js::LazyArrayBufferTable *lazyArrayBuffers;
// Inline transparent typed objects do not initially have an array buffer,
// but can have that buffer created lazily if it is accessed later. This
// table manages references from such typed objects to their buffers.
js::ObjectWeakMap *lazyArrayBuffers;
// All unboxed layouts in the compartment.
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
@ -392,16 +399,14 @@ struct JSCompartment
void fixupInitialShapeTable();
void fixupAfterMovingGC();
void fixupGlobal();
void fixupBaseShapeTable();
bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
void forgetObjectMetadataCallback() {
objectMetadataCallback = nullptr;
}
bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const {
return objectMetadataCallback(cx, obj);
}
void setNewObjectMetadata(JSContext *cx, JSObject *obj);
void clearObjectMetadata();
const void *addressOfMetadataCallback() const {
return &objectMetadataCallback;
}

View File

@ -24,6 +24,7 @@
#include "js/Proxy.h"
#include "proxy/DeadObjectProxy.h"
#include "vm/ArgumentsObject.h"
#include "vm/WeakMapObject.h"
#include "vm/WrapperObject.h"
#include "jsobjinlines.h"
@ -1173,16 +1174,13 @@ js::SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback)
cx->compartment()->setObjectMetadataCallback(callback);
}
JS_FRIEND_API(bool)
js::SetObjectMetadata(JSContext *cx, HandleObject obj, HandleObject metadata)
{
return JSObject::setMetadata(cx, obj, metadata);
}
JS_FRIEND_API(JSObject *)
js::GetObjectMetadata(JSObject *obj)
{
return obj->getMetadata();
ObjectWeakMap *map = obj->compartment()->objectMetadataTable;
if (map)
return map->lookup(obj);
return nullptr;
}
JS_FRIEND_API(bool)

View File

@ -2583,23 +2583,18 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) {
}
};
typedef bool
(* ObjectMetadataCallback)(JSContext *cx, JSObject **pmetadata);
typedef JSObject *
(* ObjectMetadataCallback)(JSContext *cx);
/*
* Specify a callback to invoke when creating each JS object in the current
* compartment, which may return a metadata object to associate with the
* object. Objects with different metadata have different shape hierarchies,
* so for efficiency, objects should generally try to share metadata objects.
* object.
*/
JS_FRIEND_API(void)
SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback);
/* Manipulate the metadata associated with an object. */
JS_FRIEND_API(bool)
SetObjectMetadata(JSContext *cx, JS::HandleObject obj, JS::HandleObject metadata);
/* Get the metadata associated with an object. */
JS_FRIEND_API(JSObject *)
GetObjectMetadata(JSObject *obj);

View File

@ -477,12 +477,8 @@ NewPropertyIteratorObject(JSContext *cx, unsigned flags)
if (!group)
return nullptr;
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cx, &metadata))
return nullptr;
const Class *clasp = &PropertyIteratorObject::class_;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr), metadata,
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
ITERATOR_FINALIZE_KIND));
if (!shape)
return nullptr;

View File

@ -949,7 +949,6 @@ js::SetIntegrityLevel(JSContext *cx, HandleObject obj, IntegrityLevel level)
// dictionary mode.
RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(),
nobj->getTaggedProto(),
nobj->getMetadata(),
nobj->numFixedSlots(),
nobj->lastProperty()->getObjectFlags()));
if (!last)
@ -1109,10 +1108,6 @@ NewObject(ExclusiveContext *cx, HandleObjectGroup group, gc::AllocKind kind,
MOZ_ASSERT_IF(clasp == &JSFunction::class_,
kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cx, &metadata))
return nullptr;
// For objects which can have fixed data following the object, only use
// enough fixed slots to cover the number of reserved slots in the object,
// regardless of the allocation kind specified.
@ -1120,8 +1115,7 @@ NewObject(ExclusiveContext *cx, HandleObjectGroup group, gc::AllocKind kind,
? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
: GetGCKindSlots(kind, clasp);
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(),
metadata, nfixed));
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed));
if (!shape)
return nullptr;
@ -1163,7 +1157,6 @@ NewObjectWithTaggedProtoIsCachable(ExclusiveContext *cxArg, Handle<TaggedProto>
proto.isObject() &&
newKind == GenericObject &&
clasp->isNative() &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback() &&
!proto.toObject()->is<GlobalObject>();
}
@ -1307,8 +1300,7 @@ NewObjectWithClassProtoIsCachable(ExclusiveContext *cxArg,
return cxArg->isJSContext() &&
protoKey != JSProto_Null &&
newKind == GenericObject &&
clasp->isNative() &&
!cxArg->asJSContext()->compartment()->hasObjectMetadataCallback();
clasp->isNative();
}
JSObject *
@ -1382,7 +1374,6 @@ NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group,
newKind == GenericObject &&
group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) &&
!cx->compartment()->hasObjectMetadataCallback() &&
cx->isJSContext();
}
@ -1885,13 +1876,9 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
assertSameCompartment(cx, src, dst);
MOZ_ASSERT(src->getClass() == dst->getClass());
MOZ_ASSERT(dst->lastProperty()->getObjectFlags() == 0);
MOZ_ASSERT(!src->getMetadata());
MOZ_ASSERT(!src->isSingleton());
MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots());
// Save the dst metadata, if any, before we start messing with its shape.
RootedObject dstMetadata(cx, dst->getMetadata());
if (!dst->ensureElements(cx, src->getDenseInitializedLength()))
return false;
@ -1908,10 +1895,9 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
} else {
// We need to generate a new shape for dst that has dst's proto but all
// the property information from src. Note that we asserted above that
// dst's object flags are 0 and we plan to set up the metadata later, so
// it's OK to pass null for the metadata here.
// dst's object flags are 0.
shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(),
nullptr, dst->numFixedSlots(), 0);
dst->numFixedSlots(), 0);
if (!shape)
return false;
@ -1937,11 +1923,6 @@ InitializePropertiesFromCompatibleNativeObject(JSContext *cx,
for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < span; i++)
dst->setSlot(i, src->getSlot(i));
if (dstMetadata) {
if (!js::SetObjectMetadata(cx, dst, dstMetadata))
return false;
}
return true;
}

View File

@ -429,10 +429,6 @@ class JSObject : public js::gc::Cell
*/
inline JSObject *enclosingScope();
/* Access the metadata on an object. */
inline JSObject *getMetadata() const;
static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata);
inline js::GlobalObject &global() const;
inline bool isOwnGlobal() const;

View File

@ -233,6 +233,24 @@ ClassCanHaveFixedData(const Class *clasp)
|| js::IsTypedArrayClass(clasp);
}
static MOZ_ALWAYS_INLINE void
SetNewObjectMetadata(ExclusiveContext *cxArg, JSObject *obj)
{
// The metadata callback is invoked for each object created on the main
// thread, except when analysis/compilation is active, to avoid recursion.
if (JSContext *cx = cxArg->maybeJSContext()) {
if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
!cx->zone()->types.activeAnalysis)
{
// Use AutoEnterAnalysis to prohibit both any GC activity under the
// callback, and any reentering of JS via Invoke() etc.
AutoEnterAnalysis enter(cx);
cx->compartment()->setNewObjectMetadata(cx, obj);
}
}
}
} // namespace js
/* static */ inline JSObject *
@ -283,6 +301,8 @@ JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::Initi
if (group->clasp()->isJSFunction())
memset(obj->as<JSFunction>().fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind));
SetNewObjectMetadata(cx, obj);
js::gc::TraceCreateObject(obj);
return obj;
@ -313,14 +333,6 @@ JSObject::setInitialElementsMaybeNonNative(js::HeapSlot *elements)
static_cast<js::NativeObject *>(this)->elements_ = elements;
}
inline JSObject *
JSObject::getMetadata() const
{
if (js::Shape *shape = maybeShape())
return shape->getObjectMetadata();
return nullptr;
}
inline js::GlobalObject &
JSObject::global() const
{
@ -828,28 +840,6 @@ Unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp)
return true;
}
static MOZ_ALWAYS_INLINE bool
NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata)
{
// The metadata callback is invoked before each created object, except when
// analysis/compilation is active, to avoid recursion. It is also skipped
// when we allocate objects during a bailout, to prevent stack iterations.
MOZ_ASSERT(!*pmetadata);
if (JSContext *cx = cxArg->maybeJSContext()) {
if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
!cx->zone()->types.activeAnalysis)
{
// Use AutoEnterAnalysis to prohibit both any GC activity under the
// callback, and any reentering of JS via Invoke() etc.
AutoEnterAnalysis enter(cx);
if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata))
return false;
}
}
return true;
}
static inline unsigned
ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
{

View File

@ -141,7 +141,7 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle
// Start with the empty shape and then append one shape per aliased binding.
RootedShape shape(cx,
EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr), nullptr,
EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
nfixed, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE));
if (!shape)
return false;
@ -164,7 +164,7 @@ Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle
return false;
#endif
StackBaseShape stackBase(cx, &CallObject::class_, nullptr,
StackBaseShape stackBase(cx, &CallObject::class_,
BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE);
UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase);

View File

@ -673,3 +673,65 @@ js::InitBareWeakMapCtor(JSContext *cx, HandleObject obj)
return InitWeakMapClass(cx, obj, false);
}
ObjectWeakMap::ObjectWeakMap(JSContext *cx)
: map(cx, nullptr)
{
if (!map.init())
CrashAtUnhandlableOOM("ObjectWeakMap");
}
ObjectWeakMap::~ObjectWeakMap()
{
WeakMapBase::removeWeakMapFromList(&map);
}
JSObject *
ObjectWeakMap::lookup(const JSObject *obj)
{
if (ObjectValueMap::Ptr p = map.lookup(const_cast<JSObject *>(obj)))
return &p->value().toObject();
return nullptr;
}
bool
ObjectWeakMap::add(JSContext *cx, JSObject *obj, JSObject *target)
{
MOZ_ASSERT(obj && target);
MOZ_ASSERT(!map.has(obj));
if (!map.put(obj, ObjectValue(*target))) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
void
ObjectWeakMap::clear()
{
map.clear();
}
void
ObjectWeakMap::trace(JSTracer *trc)
{
map.trace(trc);
}
size_t
ObjectWeakMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return map.sizeOfExcludingThis(mallocSizeOf);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void
ObjectWeakMap::checkAfterMovingGC()
{
for (ObjectValueMap::Range r = map.all(); !r.empty(); r.popFront()) {
CheckGCThingAfterMovingGC(r.front().key().get());
CheckGCThingAfterMovingGC(&r.front().value().toObject());
}
}
#endif // JSGC_HASH_TABLE_CHECKS

View File

@ -169,13 +169,8 @@ ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction calle
if (!group)
return nullptr;
JSObject *metadata = nullptr;
if (!NewObjectMetadata(cx, &metadata))
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
metadata, FINALIZE_KIND,
BaseShape::INDEXED));
FINALIZE_KIND, BaseShape::INDEXED));
if (!shape)
return nullptr;

View File

@ -51,6 +51,8 @@ ArrayObject::createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::I
static_cast<ArrayObject *>(obj)->shape_.init(shape);
static_cast<ArrayObject *>(obj)->group_.init(group);
SetNewObjectMetadata(cx, obj);
return &obj->as<ArrayObject>();
}
@ -104,7 +106,6 @@ ArrayObject::createArray(ExclusiveContext *cx, gc::InitialHeap heap,
/* static */ inline ArrayObject *
ArrayObject::createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap,
HandleShape shape,
HandleArrayObject sharedElementsOwner)
{
MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->isCopyOnWrite());
@ -115,6 +116,7 @@ ArrayObject::createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap,
// its fixed elements.
gc::AllocKind kind = gc::AllocKind::OBJECT0_BACKGROUND;
RootedShape shape(cx, sharedElementsOwner->lastProperty());
RootedObjectGroup group(cx, sharedElementsOwner->group());
ArrayObject *obj = createArrayInternal(cx, kind, heap, shape, group);
if (!obj)

View File

@ -60,7 +60,6 @@ class ArrayObject : public NativeObject
static inline ArrayObject *
createCopyOnWriteArray(ExclusiveContext *cx,
gc::InitialHeap heap,
HandleShape shape,
HandleArrayObject sharedElementsOwner);
private:

View File

@ -3227,6 +3227,9 @@ Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
if (trackingAllocationSites)
global->compartment()->forgetObjectMetadataCallback();
// Clear out all object metadata in the compartment.
global->compartment()->clearObjectMetadata();
if (global->getDebuggers()->empty()) {
global->compartment()->unsetIsDebuggee();
} else {
@ -6744,7 +6747,7 @@ DebuggerObject_getAllocationSite(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get allocationSite", args, obj);
RootedObject metadata(cx, obj->getMetadata());
RootedObject metadata(cx, GetObjectMetadata(obj));
if (!cx->compartment()->wrap(cx, &metadata))
return false;
args.rval().setObjectOrNull(metadata);

View File

@ -187,7 +187,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
friend class mozilla::LinkedListElement<Debugger>;
friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
friend bool (::JS::dbg::IsDebugger)(JS::Value val);
friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
friend JSObject *SavedStacksMetadataCallback(JSContext *cx);
friend void JS::dbg::onNewPromise(JSContext *cx, HandleObject promise);
friend void JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise);

View File

@ -338,6 +338,7 @@ StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
&cStats.compartmentTables,
&cStats.innerViewsTable,
&cStats.lazyArrayBuffersTable,
&cStats.objectMetadataTable,
&cStats.crossCompartmentWrappersTable,
&cStats.regexpCompartment,
&cStats.savedStacksSet);

View File

@ -54,8 +54,7 @@ NativeObject::canRemoveLastProperty()
*/
MOZ_ASSERT(!inDictionaryMode());
Shape *previous = lastProperty()->previous().get();
return previous->getObjectMetadata() == lastProperty()->getObjectMetadata()
&& previous->getObjectFlags() == lastProperty()->getObjectFlags();
return previous->getObjectFlags() == lastProperty()->getObjectFlags();
}
inline void
@ -331,11 +330,8 @@ CopyInitializerObject(JSContext *cx, HandlePlainObject baseobj, NewObjectKind ne
if (!obj)
return nullptr;
RootedObject metadata(cx, obj->getMetadata());
if (!obj->setLastProperty(cx, baseobj->lastProperty()))
return nullptr;
if (metadata && !JSObject::setMetadata(cx, obj, metadata))
return nullptr;
return obj;
}

View File

@ -15,6 +15,8 @@
#include "gc/GCTrace.h"
#include "vm/Probes.h"
#include "jsobjinlines.h"
namespace js {
inline bool
@ -41,9 +43,6 @@ NewObjectCache::fillGlobal(EntryIndex entry, const Class *clasp, GlobalObject *g
inline NativeObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, gc::InitialHeap heap)
{
// The new object cache does not account for metadata attached via callbacks.
MOZ_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
MOZ_ASSERT(unsigned(entryIndex) < mozilla::ArrayLength(entries));
Entry *entry = &entries[entryIndex];
@ -65,6 +64,9 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, gc::Initi
return nullptr;
copyCachedToObject(obj, templateObj, entry->kind);
SetNewObjectMetadata(cx, obj);
probes::CreateObject(cx, obj);
gc::TraceCreateObject(obj);
return obj;

View File

@ -1160,18 +1160,18 @@ SavedStacks::chooseSamplingProbability(JSContext *cx)
allocationSamplingProbability = allocationTrackingDbg->allocationSamplingProbability;
}
bool
SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata)
JSObject *
SavedStacksMetadataCallback(JSContext *cx)
{
SavedStacks &stacks = cx->compartment()->savedStacks();
if (stacks.allocationSkipCount > 0) {
stacks.allocationSkipCount--;
return true;
return nullptr;
}
stacks.chooseSamplingProbability(cx);
if (stacks.allocationSamplingProbability == 0.0)
return true;
return nullptr;
// If the sampling probability is set to 1.0, we are always taking a sample
// and can therefore leave allocationSkipCount at 0.
@ -1200,10 +1200,12 @@ SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata)
RootedSavedFrame frame(cx);
if (!stacks.saveCurrentStack(cx, &frame))
return false;
*pmetadata = frame;
CrashAtUnhandlableOOM("SavedStacksMetadataCallback");
return Debugger::onLogAllocationSite(cx, frame, PRMJ_Now());
if (!Debugger::onLogAllocationSite(cx, frame, PRMJ_Now()))
CrashAtUnhandlableOOM("SavedStacksMetadataCallback");
return frame;
}
JS_FRIEND_API(JSPrincipals *)

View File

@ -125,7 +125,7 @@ struct SavedFrame::HashPolicy
};
class SavedStacks {
friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
friend JSObject *SavedStacksMetadataCallback(JSContext *cx);
public:
SavedStacks()
@ -243,7 +243,7 @@ class SavedStacks {
bool getLocation(JSContext *cx, const FrameIter &iter, MutableHandleLocationValue locationp);
};
bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata);
JSObject *SavedStacksMetadataCallback(JSContext *cx);
} /* namespace js */

View File

@ -16,6 +16,7 @@
#include "vm/GlobalObject.h"
#include "vm/ProxyObject.h"
#include "vm/Shape.h"
#include "vm/WeakMapObject.h"
#include "vm/Xdr.h"
#include "jsatominlines.h"
@ -332,8 +333,7 @@ DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::Initi
RootedShape emptyDeclEnvShape(cx);
emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
nullptr, FINALIZE_KIND,
BaseShape::DELEGATE);
FINALIZE_KIND, BaseShape::DELEGATE);
if (!emptyDeclEnvShape)
return nullptr;
@ -405,7 +405,7 @@ StaticWithObject::create(ExclusiveContext *cx)
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
nullptr, FINALIZE_KIND));
FINALIZE_KIND));
if (!shape)
return nullptr;
@ -439,7 +439,7 @@ DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclo
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith),
nullptr, FINALIZE_KIND));
FINALIZE_KIND));
if (!shape)
return nullptr;
@ -572,8 +572,7 @@ StaticEvalObject::create(JSContext *cx, HandleObject enclosing)
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
nullptr, FINALIZE_KIND,
BaseShape::DELEGATE));
FINALIZE_KIND, BaseShape::DELEGATE));
if (!shape)
return nullptr;
@ -672,7 +671,7 @@ StaticBlockObject::create(ExclusiveContext *cx)
RootedShape emptyBlockShape(cx);
emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, TaggedProto(nullptr),
nullptr, FINALIZE_KIND, BaseShape::DELEGATE);
FINALIZE_KIND, BaseShape::DELEGATE);
if (!emptyBlockShape)
return nullptr;
@ -883,7 +882,7 @@ UninitializedLexicalObject::create(JSContext *cx, HandleObject enclosing)
return nullptr;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
nullptr, FINALIZE_KIND));
FINALIZE_KIND));
if (!shape)
return nullptr;
@ -1806,14 +1805,6 @@ js::IsDebugScopeSlow(ProxyObject *proxy)
/*****************************************************************************/
/* static */ MOZ_ALWAYS_INLINE void
DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
const PreBarrieredObject &key)
{
if (key && IsInsideNursery(key))
rt->gc.storeBuffer.putGeneric(UnbarrieredRef(map, key.get()));
}
/* static */ MOZ_ALWAYS_INLINE void
DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key)
{
@ -1837,19 +1828,12 @@ DebugScopes::DebugScopes(JSContext *cx)
DebugScopes::~DebugScopes()
{
MOZ_ASSERT(missingScopes.empty());
WeakMapBase::removeWeakMapFromList(&proxiedScopes);
}
bool
DebugScopes::init()
{
if (!liveScopes.init() ||
!proxiedScopes.init() ||
!missingScopes.init())
{
return false;
}
return true;
return liveScopes.init() && missingScopes.init();
}
void
@ -1921,10 +1905,7 @@ DebugScopes::checkHashTablesAfterMovingGC(JSRuntime *runtime)
* postbarriers have worked and that no hashtable keys (or values) are left
* pointing into the nursery.
*/
for (ObjectWeakMap::Range r = proxiedScopes.all(); !r.empty(); r.popFront()) {
CheckGCThingAfterMovingGC(r.front().key().get());
CheckGCThingAfterMovingGC(r.front().value().get());
}
proxiedScopes.checkAfterMovingGC();
for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) {
CheckGCThingAfterMovingGC(r.front().key().staticScope());
CheckGCThingAfterMovingGC(r.front().value().get());
@ -1974,9 +1955,9 @@ DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope)
if (!scopes)
return nullptr;
if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) {
if (JSObject *obj = scopes->proxiedScopes.lookup(&scope)) {
MOZ_ASSERT(CanUseDebugScopeMaps(cx));
return &p->value()->as<DebugScopeObject>();
return &obj->as<DebugScopeObject>();
}
return nullptr;
@ -1995,14 +1976,7 @@ DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &
if (!scopes)
return false;
MOZ_ASSERT(!scopes->proxiedScopes.has(&scope));
if (!scopes->proxiedScopes.put(&scope, &debugScope)) {
ReportOutOfMemory(cx);
return false;
}
proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope);
return true;
return scopes->proxiedScopes.add(cx, &scope, &debugScope);
}
DebugScopeObject *
@ -2083,8 +2057,8 @@ DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
CallObject &callobj = frame.scopeChain()->as<CallObject>();
scopes->liveScopes.remove(&callobj);
if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
debugScope = &p->value()->as<DebugScopeObject>();
if (JSObject *obj = scopes->proxiedScopes.lookup(&callobj))
debugScope = &obj->as<DebugScopeObject>();
} else {
ScopeIter si(cx, frame, frame.script()->main());
if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {

View File

@ -14,6 +14,7 @@
#include "gc/Barrier.h"
#include "vm/ArgumentsObject.h"
#include "vm/ProxyObject.h"
#include "vm/WeakMapObject.h"
namespace js {
@ -913,10 +914,7 @@ class DebugScopeObject : public ProxyObject
class DebugScopes
{
/* The map from (non-debug) scopes to debug scopes. */
typedef WeakMap<PreBarrieredObject, RelocatablePtrObject> ObjectWeakMap;
ObjectWeakMap proxiedScopes;
static MOZ_ALWAYS_INLINE void proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
const PreBarrieredObject &key);
/*
* The map from live frames which have optimized-away scopes to the

View File

@ -24,11 +24,9 @@
namespace js {
inline
StackBaseShape::StackBaseShape(ExclusiveContext *cx, const Class *clasp,
JSObject *metadata, uint32_t objectFlags)
StackBaseShape::StackBaseShape(ExclusiveContext *cx, const Class *clasp, uint32_t objectFlags)
: flags(objectFlags),
clasp(clasp),
metadata(metadata),
compartment(cx->compartment_)
{}

View File

@ -323,8 +323,7 @@ Shape::replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base,
if (!shape->parent) {
/* Treat as resetting the initial property of the shape hierarchy. */
AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
return EmptyShape::getInitialShape(cx, base.clasp, proto,
base.metadata, kind,
return EmptyShape::getInitialShape(cx, base.clasp, proto, kind,
base.flags & BaseShape::OBJECT_FLAG_MASK);
}
@ -624,8 +623,7 @@ js::ReshapeForAllocKind(JSContext *cx, Shape *shape, TaggedProto proto,
// Construct the new shape, without updating type information.
RootedId id(cx);
RootedShape newShape(cx, EmptyShape::getInitialShape(cx, shape->getObjectClass(),
proto, shape->getObjectMetadata(),
nfixed, shape->getObjectFlags()));
proto, nfixed, shape->getObjectFlags()));
for (unsigned i = 0; i < ids.length(); i++) {
id = ids[i];
@ -1130,45 +1128,6 @@ NativeObject::shadowingShapeChange(ExclusiveContext *cx, const Shape &shape)
return generateOwnShape(cx);
}
/* static */ bool
JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata)
{
if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
StackBaseShape base(obj->as<NativeObject>().lastProperty());
base.metadata = metadata;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
return true;
}
Shape *existingShape = obj->ensureShape(cx);
if (!existingShape)
return false;
Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), existingShape);
if (!newShape)
return false;
obj->setShapeMaybeNonNative(newShape);
return true;
}
/* static */ Shape *
Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last)
{
if (last->getObjectMetadata() == metadata)
return last;
StackBaseShape base(last);
base.metadata = metadata;
RootedShape lastRoot(cx, last);
return replaceLastProperty(cx, base, proto, lastRoot);
}
bool
JSObject::setFlags(ExclusiveContext *cx, BaseShape::Flag flags, GenerateShape generateShape)
{
@ -1238,74 +1197,13 @@ StackBaseShape::hash(const Lookup& lookup)
{
HashNumber hash = lookup.flags;
hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.clasp) >> 3);
hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.hashMetadata) >> 3);
return hash;
}
/* static */ inline bool
StackBaseShape::match(UnownedBaseShape *key, const Lookup& lookup)
{
return key->flags == lookup.flags
&& key->clasp_ == lookup.clasp
&& key->metadata == lookup.matchMetadata;
}
void
StackBaseShape::trace(JSTracer *trc)
{
// No need to trace the global, since our compartment had better
// have been entered.
MOZ_ASSERT(compartment->hasBeenEntered());
if (metadata)
gc::MarkObjectRoot(trc, (JSObject**)&metadata, "StackBaseShape metadata");
}
/*
* This class is used to add a post barrier on the baseShapes set, as the key is
* calculated based on objects which may be moved by generational GC.
*/
class BaseShapeSetRef : public BufferableRef
{
BaseShapeSet *set;
UnownedBaseShape *base;
JSObject *metadataPrior;
public:
BaseShapeSetRef(BaseShapeSet *set, UnownedBaseShape *base)
: set(set),
base(base),
metadataPrior(base->getObjectMetadata())
{
MOZ_ASSERT(!base->isOwned());
}
void mark(JSTracer *trc) {
JSObject *metadata = metadataPrior;
if (metadata)
Mark(trc, &metadata, "baseShapes set metadata");
if (metadata == metadataPrior)
return;
StackBaseShape::Lookup lookupPrior(base->getObjectFlags(),
base->clasp(),
metadataPrior, metadata);
ReadBarriered<UnownedBaseShape *> b(base);
MOZ_ALWAYS_TRUE(set->rekeyAs(lookupPrior, base, b));
}
};
static void
BaseShapesTablePostBarrier(ExclusiveContext *cx, BaseShapeSet *table, UnownedBaseShape *base)
{
if (!cx->isJSContext()) {
MOZ_ASSERT(!IsInsideNursery(base->getObjectMetadata()));
return;
}
if (IsInsideNursery(base->getObjectMetadata())) {
StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer;
sb.putGeneric(BaseShapeSetRef(table, base));
}
return key->flags == lookup.flags && key->clasp_ == lookup.clasp;
}
/* static */ UnownedBaseShape*
@ -1320,8 +1218,6 @@ BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base)
if (p)
return *p;
RootedGeneric<StackBaseShape*> root(cx, &base);
BaseShape *nbase_ = Allocate<BaseShape>(cx);
if (!nbase_)
return nullptr;
@ -1333,8 +1229,6 @@ BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base)
if (!p.add(cx, table, base, nbase))
return nullptr;
BaseShapesTablePostBarrier(cx, &table, nbase);
return nbase;
}
@ -1344,37 +1238,11 @@ BaseShape::assertConsistency()
#ifdef DEBUG
if (isOwned()) {
UnownedBaseShape *unowned = baseUnowned();
MOZ_ASSERT(getObjectMetadata() == unowned->getObjectMetadata());
MOZ_ASSERT(getObjectFlags() == unowned->getObjectFlags());
}
#endif
}
bool
BaseShape::fixupBaseShapeTableEntry()
{
if (metadata && IsForwarded(metadata.get())) {
metadata = Forwarded(metadata.get());
return true;
}
return false;
}
void
JSCompartment::fixupBaseShapeTable()
{
if (!baseShapes.initialized())
return;
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
UnownedBaseShape *base = e.front().unbarrieredGet();
if (base->fixupBaseShapeTableEntry()) {
ReadBarriered<UnownedBaseShape *> b(base);
e.rekeyFront(base, b);
}
}
}
void
JSCompartment::sweepBaseShapeTable()
{
@ -1383,7 +1251,6 @@ JSCompartment::sweepBaseShapeTable()
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
UnownedBaseShape *base = e.front().unbarrieredGet();
MOZ_ASSERT_IF(base->getObjectMetadata(), !IsForwarded(base->getObjectMetadata()));
if (IsBaseShapeAboutToBeFinalizedFromAnyThread(&base)) {
e.removeFront();
} else if (base != e.front().unbarrieredGet()) {
@ -1404,8 +1271,6 @@ JSCompartment::checkBaseShapeTableAfterMovingGC()
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
UnownedBaseShape *base = e.front().unbarrieredGet();
CheckGCThingAfterMovingGC(base);
if (base->getObjectMetadata())
CheckGCThingAfterMovingGC(base->getObjectMetadata());
BaseShapeSet::Ptr ptr = baseShapes.lookup(base);
MOZ_ASSERT(ptr.found() && &*ptr == &e.front());
@ -1437,8 +1302,7 @@ InitialShapeEntry::InitialShapeEntry(const ReadBarrieredShape &shape, TaggedProt
inline InitialShapeEntry::Lookup
InitialShapeEntry::getLookup() const
{
return Lookup(shape->getObjectClass(), proto, shape->getObjectMetadata(),
shape->numFixedSlots(), shape->getObjectFlags());
return Lookup(shape->getObjectClass(), proto, shape->numFixedSlots(), shape->getObjectFlags());
}
/* static */ inline HashNumber
@ -1447,8 +1311,6 @@ InitialShapeEntry::hash(const Lookup &lookup)
HashNumber hash = uintptr_t(lookup.clasp) >> 3;
hash = RotateLeft(hash, 4) ^
(uintptr_t(lookup.hashProto.toWord()) >> 3);
hash = RotateLeft(hash, 4) ^
(uintptr_t(lookup.hashMetadata) >> 3);
return hash + lookup.nfixed;
}
@ -1458,21 +1320,19 @@ InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
const Shape *shape = *key.shape.unsafeGet();
return lookup.clasp == shape->getObjectClass()
&& lookup.matchProto.toWord() == key.proto.toWord()
&& lookup.matchMetadata == shape->getObjectMetadata()
&& lookup.nfixed == shape->numFixedSlots()
&& lookup.baseFlags == shape->getObjectFlags();
}
/*
* This class is used to add a post barrier on the initialShapes set, as the key
* is calculated based on several objects which may be moved by generational GC.
* is calculated based on objects which may be moved by generational GC.
*/
class InitialShapeSetRef : public BufferableRef
{
InitialShapeSet *set;
const Class *clasp;
TaggedProto proto;
JSObject *metadata;
size_t nfixed;
uint32_t objectFlags;
@ -1480,31 +1340,24 @@ class InitialShapeSetRef : public BufferableRef
InitialShapeSetRef(InitialShapeSet *set,
const Class *clasp,
TaggedProto proto,
JSObject *metadata,
size_t nfixed,
uint32_t objectFlags)
: set(set),
clasp(clasp),
proto(proto),
metadata(metadata),
nfixed(nfixed),
objectFlags(objectFlags)
{}
void mark(JSTracer *trc) {
TaggedProto priorProto = proto;
JSObject *priorMetadata = metadata;
if (proto.isObject())
Mark(trc, reinterpret_cast<JSObject**>(&proto), "initialShapes set proto");
if (metadata)
Mark(trc, &metadata, "initialShapes set metadata");
if (proto == priorProto && metadata == priorMetadata)
if (proto == priorProto)
return;
/* Find the original entry, which must still be present. */
InitialShapeEntry::Lookup lookup(clasp, priorProto,
priorMetadata, metadata,
nfixed, objectFlags);
InitialShapeEntry::Lookup lookup(clasp, priorProto, nfixed, objectFlags);
InitialShapeSet::Ptr p = set->lookup(lookup);
MOZ_ASSERT(p);
@ -1515,7 +1368,7 @@ class InitialShapeSetRef : public BufferableRef
/* Rekey the entry. */
set->rekeyAs(lookup,
InitialShapeEntry::Lookup(clasp, proto, metadata, nfixed, objectFlags),
InitialShapeEntry::Lookup(clasp, proto, nfixed, objectFlags),
*p);
}
};
@ -1540,12 +1393,9 @@ JSCompartment::checkInitialShapesTableAfterMovingGC()
if (proto.isObject())
CheckGCThingAfterMovingGC(proto.toObject());
if (shape->getObjectMetadata())
CheckGCThingAfterMovingGC(shape->getObjectMetadata());
InitialShapeEntry::Lookup lookup(shape->getObjectClass(),
proto,
shape->getObjectMetadata(),
shape->numFixedSlots(),
shape->getObjectFlags());
InitialShapeSet::Ptr ptr = initialShapes.lookup(lookup);
@ -1570,7 +1420,7 @@ EmptyShape::new_(ExclusiveContext *cx, Handle<UnownedBaseShape *> base, uint32_t
/* static */ Shape *
EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto,
JSObject *metadata, size_t nfixed, uint32_t objectFlags)
size_t nfixed, uint32_t objectFlags)
{
MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
@ -1581,14 +1431,13 @@ EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProt
typedef InitialShapeEntry::Lookup Lookup;
DependentAddPtr<InitialShapeSet>
p(cx, table, Lookup(clasp, proto, metadata, nfixed, objectFlags));
p(cx, table, Lookup(clasp, proto, nfixed, objectFlags));
if (p)
return p->shape;
Rooted<TaggedProto> protoRoot(cx, proto);
RootedObject metadataRoot(cx, metadata);
StackBaseShape base(cx, clasp, metadata, objectFlags);
StackBaseShape base(cx, clasp, objectFlags);
Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base));
if (!nbase)
return nullptr;
@ -1597,17 +1446,14 @@ EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProt
if (!shape)
return nullptr;
Lookup lookup(clasp, protoRoot, metadataRoot, nfixed, objectFlags);
Lookup lookup(clasp, protoRoot, nfixed, objectFlags);
if (!p.add(cx, table, lookup, InitialShapeEntry(ReadBarrieredShape(shape), protoRoot)))
return nullptr;
// Post-barrier for the initial shape table update.
if (cx->isJSContext()) {
if ((protoRoot.isObject() && IsInsideNursery(protoRoot.toObject())) ||
IsInsideNursery(metadataRoot.get()))
{
InitialShapeSetRef ref(
&table, clasp, protoRoot, metadataRoot, nfixed, objectFlags);
if (protoRoot.isObject() && IsInsideNursery(protoRoot.toObject())) {
InitialShapeSetRef ref(&table, clasp, protoRoot, nfixed, objectFlags);
cx->asJSContext()->runtime()->gc.storeBuffer.putGeneric(ref);
}
}
@ -1617,9 +1463,9 @@ EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProt
/* static */ Shape *
EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto,
JSObject *metadata, AllocKind kind, uint32_t objectFlags)
AllocKind kind, uint32_t objectFlags)
{
return getInitialShape(cx, clasp, proto, metadata, GetGCKindSlots(kind, clasp), objectFlags);
return getInitialShape(cx, clasp, proto, GetGCKindSlots(kind, clasp), objectFlags);
}
void
@ -1647,7 +1493,6 @@ NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, Hand
EmptyShape::insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto)
{
InitialShapeEntry::Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
shape->getObjectMetadata(),
shape->numFixedSlots(), shape->getObjectFlags());
InitialShapeSet::Ptr p = cx->compartment()->initialShapes.lookup(lookup);
@ -1721,15 +1566,9 @@ JSCompartment::fixupInitialShapeTable()
entry.proto = TaggedProto(Forwarded(entry.proto.toObject()));
needRekey = true;
}
JSObject *metadata = entry.shape->getObjectMetadata();
if (metadata) {
metadata = MaybeForwarded(metadata);
needRekey = true;
}
if (needRekey) {
InitialShapeEntry::Lookup relookup(entry.shape->getObjectClass(),
entry.proto,
metadata,
entry.shape->numFixedSlots(),
entry.shape->getObjectFlags());
e.rekeyFront(relookup, entry);

View File

@ -407,8 +407,6 @@ class BaseShape : public gc::TenuredCell
private:
const Class *clasp_; /* Class of referring object. */
HeapPtrObject metadata; /* Optional holder of metadata about
* the referring object. */
JSCompartment *compartment_; /* Compartment shape belongs to. */
uint32_t flags; /* Vector of above flags. */
uint32_t slotSpan_; /* Object slot span for BaseShapes at
@ -420,38 +418,16 @@ class BaseShape : public gc::TenuredCell
/* For owned BaseShapes, the shape's shape table. */
ShapeTable *table_;
/*
* Our size needs to be a multiple of gc::CellSize, which happens
* to be 8. On 32-bit, the above members leave us at 28 bytes, so
* we have to throw in a bit of padding.
*/
#ifndef HAVE_64BIT_BUILD
uint32_t unusedPadding_;
#endif
BaseShape(const BaseShape &base) = delete;
public:
void finalize(FreeOp *fop);
BaseShape(JSCompartment *comp, const Class *clasp, JSObject *metadata,
uint32_t objectFlags)
BaseShape(JSCompartment *comp, const Class *clasp, uint32_t objectFlags)
{
MOZ_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp_ = clasp;
this->metadata = metadata;
this->flags = objectFlags;
this->compartment_ = comp;
}
BaseShape(JSCompartment *comp, const Class *clasp, JSObject *metadata,
uint32_t objectFlags, uint8_t attrs)
{
MOZ_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp_ = clasp;
this->metadata = metadata;
this->flags = objectFlags;
this->compartment_ = comp;
}
@ -463,7 +439,6 @@ class BaseShape : public gc::TenuredCell
BaseShape &operator=(const BaseShape &other) {
clasp_ = other.clasp_;
metadata = other.metadata;
flags = other.flags;
slotSpan_ = other.slotSpan_;
compartment_ = other.compartment_;
@ -481,7 +456,6 @@ class BaseShape : public gc::TenuredCell
this->unowned_ = unowned;
}
JSObject *getObjectMetadata() const { return metadata; }
uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
bool hasTable() const { MOZ_ASSERT_IF(table_, isOwned()); return table_ != nullptr; }
@ -519,7 +493,6 @@ class BaseShape : public gc::TenuredCell
void markChildren(JSTracer *trc);
void fixupAfterMovingGC() {}
bool fixupBaseShapeTableEntry();
private:
static void staticAsserts() {
@ -573,58 +546,35 @@ struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
{
uint32_t flags;
const Class *clasp;
JSObject *metadata;
JSCompartment *compartment;
explicit StackBaseShape(BaseShape *base)
: flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
clasp(base->clasp_),
metadata(base->metadata),
compartment(base->compartment())
{}
inline StackBaseShape(ExclusiveContext *cx, const Class *clasp,
JSObject *metadata, uint32_t objectFlags);
inline StackBaseShape(ExclusiveContext *cx, const Class *clasp, uint32_t objectFlags);
explicit inline StackBaseShape(Shape *shape);
struct Lookup
{
uint32_t flags;
const Class *clasp;
JSObject *hashMetadata;
JSObject *matchMetadata;
MOZ_IMPLICIT Lookup(const StackBaseShape &base)
: flags(base.flags),
clasp(base.clasp),
hashMetadata(base.metadata),
matchMetadata(base.metadata)
: flags(base.flags), clasp(base.clasp)
{}
MOZ_IMPLICIT Lookup(UnownedBaseShape *base)
: flags(base->getObjectFlags()),
clasp(base->clasp()),
hashMetadata(base->getObjectMetadata()),
matchMetadata(base->getObjectMetadata())
: flags(base->getObjectFlags()), clasp(base->clasp())
{
MOZ_ASSERT(!base->isOwned());
}
// For use by generational GC post barriers.
Lookup(uint32_t flags, const Class *clasp,
JSObject *hashMetadata, JSObject *matchMetadata)
: flags(flags),
clasp(clasp),
hashMetadata(hashMetadata),
matchMetadata(matchMetadata)
{}
};
static inline HashNumber hash(const Lookup& lookup);
static inline bool match(UnownedBaseShape *key, const Lookup& lookup);
// For RootedGeneric<StackBaseShape*>
void trace(JSTracer *trc);
};
inline
@ -632,7 +582,6 @@ BaseShape::BaseShape(const StackBaseShape &base)
{
mozilla::PodZero(this);
this->clasp_ = base.clasp;
this->metadata = base.metadata;
this->flags = base.flags;
this->compartment_ = base.compartment;
}
@ -807,10 +756,7 @@ class Shape : public gc::TenuredCell
const Class *getObjectClass() const {
return base()->clasp_;
}
JSObject *getObjectMetadata() const { return base()->metadata; }
static Shape *setObjectMetadata(JSContext *cx,
JSObject *metadata, TaggedProto proto, Shape *last);
static Shape *setObjectFlags(ExclusiveContext *cx,
BaseShape::Flag flag, TaggedProto proto, Shape *last);
@ -1103,7 +1049,6 @@ inline
StackBaseShape::StackBaseShape(Shape *shape)
: flags(shape->getObjectFlags()),
clasp(shape->getObjectClass()),
metadata(shape->getObjectMetadata()),
compartment(shape->compartment())
{}
@ -1152,11 +1097,9 @@ struct EmptyShape : public js::Shape
* shape if none was found.
*/
static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp,
TaggedProto proto, JSObject *metadata,
size_t nfixed, uint32_t objectFlags = 0);
TaggedProto proto, size_t nfixed, uint32_t objectFlags = 0);
static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp,
TaggedProto proto, JSObject *metadata,
gc::AllocKind kind, uint32_t objectFlags = 0);
TaggedProto proto, gc::AllocKind kind, uint32_t objectFlags = 0);
/*
* Reinsert an alternate initial shape, to be returned by future
@ -1203,29 +1146,12 @@ struct InitialShapeEntry
const Class *clasp;
TaggedProto hashProto;
TaggedProto matchProto;
JSObject *hashMetadata;
JSObject *matchMetadata;
uint32_t nfixed;
uint32_t baseFlags;
Lookup(const Class *clasp, TaggedProto proto, JSObject *metadata,
uint32_t nfixed, uint32_t baseFlags)
: clasp(clasp),
hashProto(proto), matchProto(proto),
hashMetadata(metadata), matchMetadata(metadata),
nfixed(nfixed), baseFlags(baseFlags)
{}
/*
* For use by generational GC post barriers. Look up an entry whose
* metadata field may have been moved, but was hashed with the original
* values.
*/
Lookup(const Class *clasp, TaggedProto proto,
JSObject *hashMetadata, JSObject *matchMetadata,
uint32_t nfixed, uint32_t baseFlags)
Lookup(const Class *clasp, TaggedProto proto, uint32_t nfixed, uint32_t baseFlags)
: clasp(clasp),
hashProto(proto), matchProto(proto),
hashMetadata(hashMetadata), matchMetadata(matchMetadata),
nfixed(nfixed), baseFlags(baseFlags)
{}
};

View File

@ -3595,8 +3595,7 @@ TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate,
Shape *shape = obj->lastProperty();
if (shape->inDictionary() ||
!OnlyHasDataProperties(shape) ||
shape->getObjectFlags() != 0 ||
shape->getObjectMetadata() != nullptr)
shape->getObjectFlags() != 0)
{
return true;
}

View File

@ -460,8 +460,7 @@ UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
}
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto,
nullptr, nfixed, 0));
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0));
if (!shape)
return false;
@ -1119,7 +1118,6 @@ js::TryConvertToUnboxedLayout(ExclusiveContext *cx, Shape *templateShape,
// Get an empty shape which we can use for the preliminary objects.
Shape *newShape = EmptyShape::getInitialShape(cx, &UnboxedPlainObject::class_,
group->proto(),
templateShape->getObjectMetadata(),
templateShape->getObjectFlags());
if (!newShape) {
cx->recoverFromOutOfMemory();

View File

@ -29,6 +29,31 @@ class WeakMapObject : public NativeObject
ObjectValueMap *getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
};
// Generic weak map for mapping objects to other objects.
class ObjectWeakMap
{
private:
ObjectValueMap map;
public:
explicit ObjectWeakMap(JSContext *cx);
~ObjectWeakMap();
JSObject *lookup(const JSObject *obj);
bool add(JSContext *cx, JSObject *obj, JSObject *target);
void clear();
void trace(JSTracer *trc);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void checkAfterMovingGC();
#endif
};
} // namespace js
#endif /* vm_WeakMapObject_h */

View File

@ -2336,6 +2336,10 @@ ReportCompartmentStats(const JS::CompartmentStats &cStats,
cStats.lazyArrayBuffersTable,
"The table for typed object lazy array buffers.");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("object-metadata"),
cStats.objectMetadataTable,
"The table used by debugging tools for tracking object metadata");
ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
cStats.crossCompartmentWrappersTable,
"The cross-compartment wrapper table.");