mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 1143256 - Store object metadata using a weak map, r=luke.
This commit is contained in:
parent
e3f8274ddc
commit
533750696e
@ -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)
|
||||
|
@ -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."),
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -1,8 +1,4 @@
|
||||
|
||||
x = [1,2,3];
|
||||
setObjectMetadata(x, {y:0});
|
||||
assertEq(getObjectMetadata(x).y, 0);
|
||||
|
||||
setObjectMetadataCallback(true);
|
||||
|
||||
function Foo() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -60,7 +60,6 @@ class ArrayObject : public NativeObject
|
||||
static inline ArrayObject *
|
||||
createCopyOnWriteArray(ExclusiveContext *cx,
|
||||
gc::InitialHeap heap,
|
||||
HandleShape shape,
|
||||
HandleArrayObject sharedElementsOwner);
|
||||
|
||||
private:
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 *)
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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))) {
|
||||
|
@ -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
|
||||
|
@ -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_)
|
||||
{}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
||||
|
@ -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.");
|
||||
|
Loading…
Reference in New Issue
Block a user