author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 20 Mar 2015 16:07:42 -0400 | |
changeset 263659 | 324071d6d325ad1ecd6aa14e91e010734c1bd28a |
parent 263658 | c89f330bf7818b3150efc971e35b5ffe450c835f |
child 263660 | 7ff3c70a5ad29d6aff232a7c12a60f929fa5af73 |
push id | 4718 |
push user | raliiev@mozilla.com |
push date | Mon, 11 May 2015 18:39:53 +0000 |
treeherder | mozilla-beta@c20c4ef55f08 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1143256 |
milestone | 39.0a1 |
backs out | d3c9b899f7d205a33b53ec9c11d41955955c6089 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -594,17 +594,16 @@ struct CompartmentStats macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ macro(Other, NotLiveGCThing, compartmentObject) \ 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) CompartmentStats() : FOR_EACH_SIZE(ZERO_SIZE) classInfo(), extra(),
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -1384,59 +1384,60 @@ DisplayName(JSContext *cx, unsigned argc } JSFunction *fun = &args[0].toObject().as<JSFunction>(); JSString *str = fun->displayAtom(); args.rval().setString(str ? str : cx->runtime()->emptyString); return true; } -static JSObject * -ShellObjectMetadataCallback(JSContext *cx) +static bool +ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata) { RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx)); if (!obj) - CrashAtUnhandlableOOM("ShellObjectMetadataCallback"); + return false; RootedObject stack(cx, NewDenseEmptyArray(cx)); if (!stack) - CrashAtUnhandlableOOM("ShellObjectMetadataCallback"); + return false; static int createdIndex = 0; createdIndex++; if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0, JS_STUBGETTER, JS_STUBSETTER)) { - CrashAtUnhandlableOOM("ShellObjectMetadataCallback"); + return false; } if (!JS_DefineProperty(cx, obj, "stack", stack, 0, JS_STUBGETTER, JS_STUBSETTER)) { - CrashAtUnhandlableOOM("ShellObjectMetadataCallback"); + return false; } int stackIndex = 0; RootedId id(cx); RootedValue callee(cx); for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) { if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) { id = INT_TO_JSID(stackIndex); RootedObject callee(cx, iter.callee(cx)); if (!JS_DefinePropertyById(cx, stack, id, callee, 0, JS_STUBGETTER, JS_STUBSETTER)) { - CrashAtUnhandlableOOM("ShellObjectMetadataCallback"); + return false; } stackIndex++; } } - return obj; + *pmetadata = obj; + return true; } static bool SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); bool enabled = args.length() ? ToBoolean(args[0]) : false; @@ -1454,19 +1455,17 @@ SetObjectMetadata(JSContext *cx, unsigne 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()); - SetObjectMetadata(cx, obj, metadata); - - return true; + return SetObjectMetadata(cx, obj, metadata); } static bool GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() != 1 || !args[0].isObject()) { JS_ReportError(cx, "Argument must be an object");
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -14,17 +14,16 @@ #include "jsutil.h" #include "gc/Marking.h" #include "js/Vector.h" #include "vm/GlobalObject.h" #include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/TypedArrayObject.h" -#include "vm/WeakMapObject.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" #include "vm/Shape-inl.h" using mozilla::AssertedCast; @@ -1374,21 +1373,22 @@ TypedObject::typedMemBase() const return owner.as<ArrayBufferObject>().dataPointer(); return owner.as<InlineTypedObject>().inlineTypedMem(); } bool TypedObject::isAttached() const { if (is<InlineTransparentTypedObject>()) { - ObjectWeakMap *table = compartment()->lazyArrayBuffers; + LazyArrayBufferTable *table = compartment()->lazyArrayBuffers; if (table) { - JSObject *buffer = table->lookup(this); + ArrayBufferObject *buffer = + table->maybeBuffer(&const_cast<TypedObject *>(this)->as<InlineTransparentTypedObject>()); if (buffer) - return !buffer->as<ArrayBufferObject>().isNeutered(); + return !buffer->isNeutered(); } return true; } if (is<InlineOpaqueTypedObject>()) return true; if (!as<OutlineTypedObject>().outOfLineTypedMem()) return false; JSObject &owner = as<OutlineTypedObject>().owner(); @@ -2188,70 +2188,124 @@ InlineTypedObject::objectMovedDuringMino trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, oldData, newData, size_t(descr.size()) >= sizeof(uintptr_t)); } } ArrayBufferObject * InlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx) { - ObjectWeakMap *&table = cx->compartment()->lazyArrayBuffers; + LazyArrayBufferTable *&table = cx->compartment()->lazyArrayBuffers; if (!table) { - table = cx->new_<ObjectWeakMap>(cx); + table = cx->new_<LazyArrayBufferTable>(cx); if (!table) return nullptr; } - JSObject *obj = table->lookup(this); - if (obj) - return &obj->as<ArrayBufferObject>(); + ArrayBufferObject *buffer = table->maybeBuffer(this); + if (buffer) + return buffer; ArrayBufferObject::BufferContents contents = ArrayBufferObject::BufferContents::createPlain(inlineTypedMem()); size_t nbytes = typeDescr().size(); // Prevent GC under ArrayBufferObject::create, which might move this object // and its contents. gc::AutoSuppressGC suppress(cx); - ArrayBufferObject *buffer = - ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData); + buffer = ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData); if (!buffer) return nullptr; // The owning object must always be the array buffer's first view. This // both prevents the memory from disappearing out from under the buffer // (the first view is held strongly by the buffer) and is used by the // buffer marking code to detect whether its data pointer needs to be // relocated. JS_ALWAYS_TRUE(buffer->addView(cx, this)); buffer->setForInlineTypedObject(); buffer->setHasTypedObjectViews(); - if (!table->add(cx, this, buffer)) + if (!table->addBuffer(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; } ArrayBufferObject * OutlineTransparentTypedObject::getOrCreateBuffer(JSContext *cx) { if (owner().is<ArrayBufferObject>()) return &owner().as<ArrayBufferObject>(); 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 */ #define DEFINE_TYPEDOBJ_CLASS(Name, Trace) \ const Class Name::class_ = { \ # Name, \ Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS, \
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -1016,16 +1016,40 @@ IsTypeDescrClass(const Class* clasp) } inline bool 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); } // namespace js template <> inline bool JSObject::is<js::SimpleTypeDescr>() const
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1064,16 +1064,19 @@ void BaseShape::markChildren(JSTracer *trc) { if (isOwned()) gc::MarkBaseShape(trc, &unowned_, "base"); JSObject* global = compartment()->unsafeUnbarrieredMaybeGlobal(); if (global) MarkObjectUnbarriered(trc, &global, "global"); + + if (metadata) + gc::MarkObject(trc, &metadata, "metadata"); } static void PushMarkStack(GCMarker *gcmarker, BaseShape *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing); MOZ_ASSERT(!IsInsideNursery(thing)); @@ -1110,16 +1113,19 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh { base->assertConsistency(); base->compartment()->mark(); 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 * unowned base shape. */ if (base->isOwned()) { UnownedBaseShape *unowned = base->baseUnowned(); MOZ_ASSERT(base->compartment() == unowned->compartment());
--- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -302,17 +302,17 @@ Mark(JSTracer *trc, JSObject **objp, con /* For use by Debugger::WeakMap's missingScopes HashKeyRef instantiation. */ inline void Mark(JSTracer *trc, NativeObject **obj, const char *name) { MarkObjectUnbarriered(trc, obj, name); } -/* For use by Debugger::WeakMap's liveScopes HashKeyRef instantiation. */ +/* For use by Debugger::WeakMap's proxiedScopes HashKeyRef instantiation. */ inline void Mark(JSTracer *trc, ScopeObject **obj, const char *name) { MarkObjectUnbarriered(trc, obj, name); } inline bool IsMarked(BarrieredBase<Value> *v)
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -19,17 +19,16 @@ #include "builtin/MapObject.h" #include "frontend/BytecodeCompiler.h" #include "gc/GCInternals.h" #include "gc/Marking.h" #include "jit/MacroAssembler.h" #include "js/HashTable.h" #include "vm/Debugger.h" #include "vm/JSONParser.h" -#include "vm/WeakMapObject.h" #include "jsgcinlines.h" #include "jsobjinlines.h" using namespace js; using namespace js::gc; using mozilla::ArrayEnd; @@ -512,19 +511,16 @@ js::gc::GCRuntime::markRuntime(JSTracer } /* Mark debug scopes, if present */ if (c->debugScopes) c->debugScopes->mark(trc); if (c->lazyArrayBuffers) c->lazyArrayBuffers->trace(trc); - - if (c->objectMetadataTable) - c->objectMetadataTable->trace(trc); } MarkInterpreterActivations(rt, trc); jit::MarkJitActivations(rt, trc); if (!isHeapMinorCollecting()) { gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_EMBEDDING);
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -85,22 +85,17 @@ InvokeFunction(JSContext *cx, HandleObje *rval = rv; return true; } JSObject * NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap, size_t ndynamic, const js::Class *clasp) { - JSObject *obj = js::Allocate<JSObject>(cx, allocKind, ndynamic, initialHeap, clasp); - if (!obj) - return nullptr; - - SetNewObjectMetadata(cx, obj); - return obj; + return js::Allocate<JSObject>(cx, allocKind, ndynamic, initialHeap, clasp); } bool CheckOverRecursed(JSContext *cx) { // We just failed the jitStackLimit check. There are two possible reasons: // - jitStackLimit was the real stack limit and we're over-recursed // - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3211,18 +3211,22 @@ CreateArrayPrototype(JSContext *cx, JSPr if (!proto) return nullptr; RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_, TaggedProto(proto))); if (!group) return nullptr; + JSObject *metadata = nullptr; + if (!NewObjectMetadata(cx, &metadata)) + return nullptr; + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto), - gc::AllocKind::OBJECT0)); + metadata, gc::AllocKind::OBJECT0)); if (!shape) return nullptr; RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::AllocKind::OBJECT4, gc::TenuredHeap, shape, group, 0)); if (!arrayProto || !JSObject::setSingleton(cx, arrayProto) || !AddLengthProperty(cx, arrayProto)) @@ -3285,17 +3289,19 @@ EnsureNewArrayElements(ExclusiveContext MOZ_ASSERT_IF(cap, !obj->hasDynamicElements()); return true; } static bool NewArrayIsCachable(ExclusiveContext *cxArg, NewObjectKind newKind) { - return cxArg->isJSContext() && newKind == GenericObject; + return cxArg->isJSContext() && + newKind == GenericObject && + !cxArg->asJSContext()->compartment()->hasObjectMetadataCallback(); } template <uint32_t maxLength> static MOZ_ALWAYS_INLINE ArrayObject * NewArray(ExclusiveContext *cxArg, uint32_t length, HandleObject protoArg, NewObjectKind newKind = GenericObject) { gc::AllocKind allocKind = GuessArrayGCKind(length); @@ -3330,23 +3336,27 @@ NewArray(ExclusiveContext *cxArg, uint32 if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) return nullptr; RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_, TaggedProto(proto))); 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), - gc::AllocKind::OBJECT0)); + metadata, gc::AllocKind::OBJECT0)); if (!shape) return nullptr; RootedArrayObject arr(cxArg, ArrayObject::createArray(cxArg, allocKind, GetInitialHeap(newKind, &ArrayObject::class_), shape, group, length)); if (!arr) return nullptr; @@ -3503,19 +3513,30 @@ js::NewDenseFullyAllocatedArrayWithTempl probes::CreateObject(cx, arr); return arr; } JSObject * js::NewDenseCopyOnWriteArray(JSContext *cx, HandleArrayObject templateObject, gc::InitialHeap heap) { + RootedShape shape(cx, templateObject->lastProperty()); + MOZ_ASSERT(!gc::IsInsideNursery(templateObject)); - ArrayObject *arr = ArrayObject::createCopyOnWriteArray(cx, heap, 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); if (!arr) return nullptr; probes::CreateObject(cx, arr); return arr; } #ifdef DEBUG
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -56,17 +56,16 @@ JSCompartment::JSCompartment(Zone *zone, data(nullptr), objectMetadataCallback(nullptr), lastAnimationTime(0), regExps(runtime_), globalWriteBarriered(false), neuteredTypedObjects(0), propertyTree(thisForCtor()), selfHostingScriptSource(nullptr), - objectMetadataTable(nullptr), lazyArrayBuffers(nullptr), gcIncomingGrayPointers(nullptr), gcWeakMapList(nullptr), gcPreserveJitCode(options.preserveJitCode()), debugModeBits(0), rngState(0), watchpointMap(nullptr), scriptCountsMap(nullptr), @@ -87,17 +86,16 @@ JSCompartment::~JSCompartment() { reportTelemetry(); js_delete(jitCompartment_); js_delete(watchpointMap); js_delete(scriptCountsMap); js_delete(debugScriptMap); js_delete(debugScopes); - js_delete(objectMetadataTable); js_delete(lazyArrayBuffers); js_free(enumerators); runtime_->numCompartments--; } bool JSCompartment::init(JSContext *cx) @@ -643,16 +641,17 @@ JSCompartment::sweepCrossCompartmentWrap } } } void JSCompartment::fixupAfterMovingGC() { fixupGlobal(); fixupInitialShapeTable(); + fixupBaseShapeTable(); objectGroups.fixupTablesAfterMovingGC(); } void JSCompartment::fixupGlobal() { GlobalObject *global = *global_.unsafeGet(); if (global) @@ -816,32 +815,29 @@ void JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, size_t *compartmentObject, size_t *compartmentTables, size_t *innerViewsArg, size_t *lazyArrayBuffersArg, - size_t *objectMetadataTablesArg, size_t *crossCompartmentWrappersArg, size_t *regexpCompartment, size_t *savedStacksSet) { *compartmentObject += mallocSizeOf(this); objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, tiArrayTypeTables, tiObjectTypeTables, compartmentTables); *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) + initialShapes.sizeOfExcludingThis(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); } void JSCompartment::reportTelemetry() {
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -126,17 +126,17 @@ typedef HashMap<CrossCompartmentKey, Rea } /* namespace js */ namespace JS { struct TypeInferenceSizes; } namespace js { class DebugScopes; -class ObjectWeakMap; +class LazyArrayBufferTable; class WeakMapBase; } struct JSCompartment { JS::CompartmentOptions options_; private: @@ -251,17 +251,16 @@ struct JSCompartment void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *tiAllocationSiteTables, size_t *tiArrayTypeTables, size_t *tiObjectTypeTables, size_t *compartmentObject, size_t *compartmentTables, size_t *innerViews, size_t *lazyArrayBuffers, - size_t *objectMetadataTables, size_t *crossCompartmentWrappers, size_t *regexpCompartment, size_t *savedStacksSet); /* * Shared scope property tree, and arena-pool for allocating its nodes. */ js::PropertyTree propertyTree; @@ -284,27 +283,21 @@ struct JSCompartment #endif /* * Lazily initialized script source object to use for scripts cloned * from the self-hosting global. */ 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; - // 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; + // Map from typed objects to array buffers lazily created for them. + js::LazyArrayBufferTable *lazyArrayBuffers; // All unboxed layouts in the compartment. mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts; /* During GC, stores the index of this compartment in rt->compartments. */ unsigned gcIndex; /* @@ -393,24 +386,25 @@ struct JSCompartment void sweepNativeIterators(); void purge(); void clearTables(); void fixupInitialShapeTable(); void fixupAfterMovingGC(); void fixupGlobal(); + void fixupBaseShapeTable(); bool hasObjectMetadataCallback() const { return objectMetadataCallback; } void setObjectMetadataCallback(js::ObjectMetadataCallback callback); void forgetObjectMetadataCallback() { objectMetadataCallback = nullptr; } - JSObject *callObjectMetadataCallback(JSContext *cx) const { - return objectMetadataCallback(cx); + bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const { + return objectMetadataCallback(cx, obj); } const void *addressOfMetadataCallback() const { return &objectMetadataCallback; } js::SavedStacks &savedStacks() { return savedStacks_; } void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone> &finder);
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -19,17 +19,16 @@ #include "jsweakmap.h" #include "jswrapper.h" #include "prmjtime.h" #include "builtin/TestingFunctions.h" #include "js/Proxy.h" #include "proxy/DeadObjectProxy.h" #include "vm/ArgumentsObject.h" -#include "vm/WeakMapObject.h" #include "vm/WrapperObject.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "vm/NativeObject-inl.h" #include "vm/ScopeObject-inl.h" @@ -1169,36 +1168,26 @@ js::AutoCTypesActivityCallback::AutoCTyp } JS_FRIEND_API(void) js::SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback) { cx->compartment()->setObjectMetadataCallback(callback); } -JS_FRIEND_API(void) -js::SetObjectMetadata(JSContext *cx, JSObject *obj, JSObject *metadata) +JS_FRIEND_API(bool) +js::SetObjectMetadata(JSContext *cx, HandleObject obj, HandleObject metadata) { - ObjectWeakMap *&map = cx->compartment()->objectMetadataTable; - if (!map) { - map = cx->new_<ObjectWeakMap>(cx); - if (!map) - CrashAtUnhandlableOOM("SetObjectMetadata"); - } - if (!map->add(cx, obj, metadata)) - CrashAtUnhandlableOOM("SetObjectMetadata"); + return JSObject::setMetadata(cx, obj, metadata); } JS_FRIEND_API(JSObject *) js::GetObjectMetadata(JSObject *obj) { - ObjectWeakMap *map = obj->compartment()->objectMetadataTable; - if (map) - return map->lookup(obj); - return nullptr; + return obj->getMetadata(); } JS_FRIEND_API(bool) js::DefineOwnProperty(JSContext *cx, JSObject *objArg, jsid idArg, JS::Handle<js::PropertyDescriptor> descriptor, ObjectOpResult &result) { RootedObject obj(cx, objArg); RootedId id(cx, idArg);
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -2578,31 +2578,32 @@ class JS_FRIEND_API(AutoCTypesActivityCa void DoEndCallback() { if (callback) { callback(cx, endType); callback = nullptr; } } }; -typedef JSObject * -(* ObjectMetadataCallback)(JSContext *cx); +typedef bool +(* ObjectMetadataCallback)(JSContext *cx, JSObject **pmetadata); /* * 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. + * object. Objects with different metadata have different shape hierarchies, + * so for efficiency, objects should generally try to share metadata objects. */ JS_FRIEND_API(void) SetObjectMetadataCallback(JSContext *cx, ObjectMetadataCallback callback); /* Manipulate the metadata associated with an object. */ -JS_FRIEND_API(void) -SetObjectMetadata(JSContext *cx, JSObject *obj, JSObject *metadata); +JS_FRIEND_API(bool) +SetObjectMetadata(JSContext *cx, JS::HandleObject obj, JS::HandleObject metadata); JS_FRIEND_API(JSObject *) GetObjectMetadata(JSObject *obj); JS_FRIEND_API(bool) GetElementsWithAdder(JSContext *cx, JS::HandleObject obj, JS::HandleObject receiver, uint32_t begin, uint32_t end, js::ElementAdder *adder);
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -472,18 +472,22 @@ static inline PropertyIteratorObject * NewPropertyIteratorObject(JSContext *cx, unsigned flags) { if (flags & JSITER_ENUMERATE) { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_, TaggedProto(nullptr))); 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), + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr), metadata, ITERATOR_FINALIZE_KIND)); if (!shape) return nullptr; JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND, GetInitialHeap(GenericObject, clasp), shape, group); if (!obj) return nullptr;
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -944,16 +944,17 @@ js::SetIntegrityLevel(JSContext *cx, Han // Seal/freeze non-dictionary objects by constructing a new shape // hierarchy mirroring the original one, which can be shared if many // objects with the same structure are sealed/frozen. If we use the // generic path below then any non-empty object will be converted to // dictionary mode. RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(), nobj->getTaggedProto(), + nobj->getMetadata(), nobj->numFixedSlots(), nobj->lastProperty()->getObjectFlags())); if (!last) return false; // Get an in-order list of the shapes in this object. AutoShapeVector shapes(cx); for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) { @@ -1103,24 +1104,29 @@ NewObject(ExclusiveContext *cx, HandleOb NewObjectKind newKind) { const Class *clasp = group->clasp(); MOZ_ASSERT(clasp != &ArrayObject::class_); 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. size_t nfixed = ClassCanHaveFixedData(clasp) ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp) : GetGCKindSlots(kind, clasp); - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed)); + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), + metadata, nfixed)); if (!shape) return nullptr; gc::InitialHeap heap = GetInitialHeap(newKind, clasp); JSObject *obj = JSObject::create(cx, kind, heap, shape, group); if (!obj) return nullptr; @@ -1152,16 +1158,17 @@ NewObjectCache::fillProto(EntryIndex ent static bool NewObjectWithTaggedProtoIsCachable(ExclusiveContext *cxArg, Handle<TaggedProto> proto, NewObjectKind newKind, const Class *clasp) { return cxArg->isJSContext() && proto.isObject() && newKind == GenericObject && clasp->isNative() && + !cxArg->asJSContext()->compartment()->hasObjectMetadataCallback() && !proto.toObject()->is<GlobalObject>(); } JSObject * js::NewObjectWithGivenTaggedProto(ExclusiveContext *cxArg, const Class *clasp, Handle<TaggedProto> proto, gc::AllocKind allocKind, NewObjectKind newKind) { @@ -1295,17 +1302,18 @@ FindProto(ExclusiveContext *cx, const js static bool NewObjectWithClassProtoIsCachable(ExclusiveContext *cxArg, JSProtoKey protoKey, NewObjectKind newKind, const Class *clasp) { return cxArg->isJSContext() && protoKey != JSProto_Null && newKind == GenericObject && - clasp->isNative(); + clasp->isNative() && + !cxArg->asJSContext()->compartment()->hasObjectMetadataCallback(); } JSObject * js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg, const Class *clasp, HandleObject protoArg, gc::AllocKind allocKind, NewObjectKind newKind) { if (protoArg) { @@ -1369,16 +1377,17 @@ js::NewObjectWithClassProtoCommon(Exclus static bool NewObjectWithGroupIsCachable(ExclusiveContext *cx, HandleObjectGroup group, NewObjectKind newKind) { return group->proto().isObject() && newKind == GenericObject && group->clasp()->isNative() && (!group->newScript() || group->newScript()->analyzed()) && + !cx->compartment()->hasObjectMetadataCallback() && cx->isJSContext(); } /* * Create a plain object with the specified group. This bypasses getNewGroup to * avoid losing creation site information for objects made by scripted 'new'. */ JSObject * @@ -1871,38 +1880,43 @@ js::DeepCloneObjectLiteral(JSContext *cx static bool InitializePropertiesFromCompatibleNativeObject(JSContext *cx, HandleNativeObject dst, HandleNativeObject src) { 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; uint32_t initialized = src->getDenseInitializedLength(); for (uint32_t i = 0; i < initialized; ++i) { dst->setDenseInitializedLength(i + 1); dst->initDenseElement(i, src->getDenseElement(i)); } MOZ_ASSERT(!src->hasPrivate()); RootedShape shape(cx); if (src->getProto() == dst->getProto()) { shape = src->lastProperty(); } 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. + // 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. shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(), - dst->numFixedSlots(), 0); + nullptr, dst->numFixedSlots(), 0); if (!shape) return false; // Get an in-order list of the shapes in the src object. AutoShapeVector shapes(cx); for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) { if (!shapes.append(&r.front())) return false; @@ -1918,16 +1932,21 @@ InitializePropertiesFromCompatibleNative } } size_t span = shape->slotSpan(); if (!dst->setLastProperty(cx, shape)) return false; 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; } JS_FRIEND_API(bool) JS_InitializePropertiesFromCompatibleNativeObject(JSContext *cx, HandleObject dst, HandleObject src) {
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -424,16 +424,20 @@ class JSObject : public js::gc::Cell /* * Get the enclosing scope of an object. When called on non-scope object, * this will just be the global (the name "enclosing scope" still applies * in this situation because non-scope objects can be on the scope chain). */ 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; /* * ES5 meta-object properties and operations. */ public:
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -228,35 +228,16 @@ ClassCanHaveFixedData(const Class *clasp // arrays we only use enough to cover the class reserved slots, so that // the remaining space in the object's allocation is available for the // buffer's data. return !clasp->isNative() || clasp == &js::ArrayBufferObject::class_ || 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); - - if (JSObject *metadata = cx->compartment()->callObjectMetadataCallback(cx)) - SetObjectMetadata(cx, obj, metadata); - } - } -} - } // namespace js /* static */ inline JSObject * JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, js::HandleShape shape, js::HandleObjectGroup group) { MOZ_ASSERT(shape && group); MOZ_ASSERT(group->clasp() == shape->getObjectClass()); @@ -297,18 +278,16 @@ JSObject::create(js::ExclusiveContext *c if (size_t span = shape->slotSpan()) obj->as<js::NativeObject>().initializeSlotRange(0, span); // JSFunction's fixed slots expect POD-style initialization. if (group->clasp()->isJSFunction()) memset(obj->as<JSFunction>().fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind)); - SetNewObjectMetadata(cx, obj); - js::gc::TraceCreateObject(obj); return obj; } inline void JSObject::setInitialShapeMaybeNonNative(js::Shape *shape) { @@ -329,16 +308,24 @@ JSObject::setInitialSlotsMaybeNonNative( } inline void 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 { /* * The global is read-barriered so that it is kept live by access through * the JSCompartment. When accessed through a JSObject, however, the global * will be already be kept live by the black JSObject's parent pointer, so * does not need to be read-barriered. @@ -836,16 +823,38 @@ Unbox(JSContext *cx, HandleObject obj, M else if (obj->is<DateObject>()) vp.set(obj->as<DateObject>().UTCTime()); else vp.setUndefined(); 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) { /* * Respect the fact that some callers may want to preserve existing attributes as much as * possible, or install defaults otherwise. */ if (attrs & JSPROP_IGNORE_ENUMERATE) {
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -136,17 +136,17 @@ Bindings::initWithTemporaryStorage(Exclu } self->aliasedBodyLevelLexicalBegin_ = aliasedBodyLevelLexicalBegin; // Put as many of nslots inline into the object header as possible. uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots)); // Start with the empty shape and then append one shape per aliased binding. RootedShape shape(cx, - EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr), + EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr), nullptr, nfixed, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE)); if (!shape) return false; #ifdef DEBUG HashSet<PropertyName *> added(cx); if (!added.init()) return false; @@ -159,17 +159,17 @@ Bindings::initWithTemporaryStorage(Exclu #ifdef DEBUG // The caller ensures no duplicate aliased names. MOZ_ASSERT(!added.has(bi->name())); if (!added.put(bi->name())) return false; #endif - StackBaseShape stackBase(cx, &CallObject::class_, + StackBaseShape stackBase(cx, &CallObject::class_, nullptr, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE); UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase); if (!base) return false; unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE |
--- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -668,70 +668,8 @@ js::InitWeakMapClass(JSContext *cx, Hand } JSObject * 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
--- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -164,18 +164,23 @@ ArgumentsObject::create(JSContext *cx, H bool strict = callee->strict(); const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto.get()))); if (!group) return nullptr; + JSObject *metadata = nullptr; + if (!NewObjectMetadata(cx, &metadata)) + return nullptr; + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto), - FINALIZE_KIND, BaseShape::INDEXED)); + metadata, FINALIZE_KIND, + BaseShape::INDEXED)); if (!shape) return nullptr; unsigned numFormals = callee->nargs(); unsigned numDeletedWords = NumWordsForBitArrayOfLength(numActuals); unsigned numArgs = Max(numActuals, numFormals); unsigned numBytes = offsetof(ArgumentsData, args) + numDeletedWords * sizeof(size_t) +
--- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -46,18 +46,16 @@ ArrayObject::createArrayInternal(Exclusi size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), group->clasp()); JSObject *obj = Allocate<JSObject>(cx, kind, nDynamicSlots, heap, group->clasp()); if (!obj) return nullptr; static_cast<ArrayObject *>(obj)->shape_.init(shape); static_cast<ArrayObject *>(obj)->group_.init(group); - SetNewObjectMetadata(cx, obj); - return &obj->as<ArrayObject>(); } /* static */ inline ArrayObject * ArrayObject::finishCreateArray(ArrayObject *obj, HandleShape shape) { size_t span = shape->slotSpan(); if (span) @@ -101,27 +99,27 @@ ArrayObject::createArray(ExclusiveContex obj->elements_ = elements; return finishCreateArray(obj, shape); } /* static */ inline ArrayObject * ArrayObject::createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap, + HandleShape shape, HandleArrayObject sharedElementsOwner) { MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->isCopyOnWrite()); MOZ_ASSERT(sharedElementsOwner->getElementsHeader()->ownerObject() == sharedElementsOwner); // Use the smallest allocation kind for the array, as it can't have any // fixed slots (see the assert in createArrayInternal) and will not be using // 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) return nullptr; obj->elements_ = sharedElementsOwner->getDenseElementsAllowCopyOnWrite(); return finishCreateArray(obj, shape);
--- a/js/src/vm/ArrayObject.h +++ b/js/src/vm/ArrayObject.h @@ -55,16 +55,17 @@ class ArrayObject : public NativeObject HandleObjectGroup group, HeapSlot *elements); // Make a copy-on-write array object which shares the elements of an // existing object. static inline ArrayObject * createCopyOnWriteArray(ExclusiveContext *cx, gc::InitialHeap heap, + HandleShape shape, HandleArrayObject sharedElementsOwner); private: // Helper for the above methods. static inline ArrayObject * createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::InitialHeap heap,
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6742,17 +6742,17 @@ DebuggerObject_getGlobal(JSContext *cx, return true; } static bool DebuggerObject_getAllocationSite(JSContext *cx, unsigned argc, Value *vp) { THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get allocationSite", args, obj); - RootedObject metadata(cx, GetObjectMetadata(obj)); + RootedObject metadata(cx, obj->getMetadata()); if (!cx->compartment()->wrap(cx, &metadata)) return false; args.rval().setObjectOrNull(metadata); return true; } static bool DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -182,17 +182,17 @@ typedef JSObject Env; class Debugger : private mozilla::LinkedListElement<Debugger> { friend class Breakpoint; friend class DebuggerMemory; friend class SavedStacks; friend class mozilla::LinkedListElement<Debugger>; friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj); friend bool (::JS::dbg::IsDebugger)(JS::Value val); - friend JSObject *SavedStacksMetadataCallback(JSContext *cx); + friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); friend void JS::dbg::onNewPromise(JSContext *cx, HandleObject promise); friend void JS::dbg::onPromiseSettled(JSContext *cx, HandleObject promise); public: enum Hook { OnDebuggerStatement, OnExceptionUnwind, OnNewScript,
--- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -333,17 +333,16 @@ StatsCompartmentCallback(JSRuntime *rt, compartment->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &cStats.typeInferenceAllocationSiteTables, &cStats.typeInferenceArrayTypeTables, &cStats.typeInferenceObjectTypeTables, &cStats.compartmentObject, &cStats.compartmentTables, &cStats.innerViewsTable, &cStats.lazyArrayBuffersTable, - &cStats.objectMetadataTable, &cStats.crossCompartmentWrappersTable, &cStats.regexpCompartment, &cStats.savedStacksSet); } static void StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena, JSGCTraceKind traceKind, size_t thingSize)
--- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -49,17 +49,18 @@ NativeObject::canRemoveLastProperty() * Check that the information about the object stored in the last * property's base shape is consistent with that stored in the previous * shape. If not consistent, then the last property cannot be removed as it * will induce a change in the object itself, and the object must be * converted to dictionary mode instead. See BaseShape comment in jsscope.h */ MOZ_ASSERT(!inDictionaryMode()); Shape *previous = lastProperty()->previous().get(); - return previous->getObjectFlags() == lastProperty()->getObjectFlags(); + return previous->getObjectMetadata() == lastProperty()->getObjectMetadata() + && previous->getObjectFlags() == lastProperty()->getObjectFlags(); } inline void NativeObject::setShouldConvertDoubleElements() { MOZ_ASSERT(is<ArrayObject>() && !hasEmptyElements()); getElementsHeader()->setShouldConvertDoubleElements(); } @@ -325,18 +326,21 @@ CopyInitializerObject(JSContext *cx, Han gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots()); allocKind = gc::GetBackgroundAllocKind(allocKind); MOZ_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->asTenured().getAllocKind()); RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind)); 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; } inline NativeObject * NewNativeObjectWithGivenTaggedProto(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto, gc::AllocKind allocKind, NewObjectKind newKind)
--- a/js/src/vm/Runtime-inl.h +++ b/js/src/vm/Runtime-inl.h @@ -10,18 +10,16 @@ #include "vm/Runtime.h" #include "jscompartment.h" #include "gc/Allocator.h" #include "gc/GCTrace.h" #include "vm/Probes.h" -#include "jsobjinlines.h" - namespace js { inline bool NewObjectCache::lookupProto(const Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry) { MOZ_ASSERT(!proto->is<GlobalObject>()); return lookup(clasp, proto, kind, pentry); } @@ -38,16 +36,19 @@ NewObjectCache::fillGlobal(EntryIndex en { //MOZ_ASSERT(global == obj->getGlobal()); return fill(entry, clasp, global, kind, obj); } 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]; NativeObject *templateObj = reinterpret_cast<NativeObject *>(&entry->templateObject); // Do an end run around JSObject::group() to avoid doing AutoUnprotectCell // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread. ObjectGroup *group = templateObj->group_; @@ -59,19 +60,16 @@ NewObjectCache::newObjectFromHit(JSConte return nullptr; NativeObject *obj = static_cast<NativeObject *>(Allocate<JSObject, NoGC>(cx, entry->kind, 0, heap, group->clasp())); if (!obj) return nullptr; copyCachedToObject(obj, templateObj, entry->kind); - - SetNewObjectMetadata(cx, obj); - probes::CreateObject(cx, obj); gc::TraceCreateObject(obj); return obj; } } /* namespace js */ #endif /* vm_Runtime_inl_h */
--- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1155,28 +1155,28 @@ SavedStacks::chooseSamplingProbability(J } if (!allocationTrackingDbg) return; allocationSamplingProbability = allocationTrackingDbg->allocationSamplingProbability; } -JSObject * -SavedStacksMetadataCallback(JSContext *cx) +bool +SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata) { SavedStacks &stacks = cx->compartment()->savedStacks(); if (stacks.allocationSkipCount > 0) { stacks.allocationSkipCount--; - return nullptr; + return true; } stacks.chooseSamplingProbability(cx); if (stacks.allocationSamplingProbability == 0.0) - return nullptr; + return true; // If the sampling probability is set to 1.0, we are always taking a sample // and can therefore leave allocationSkipCount at 0. if (stacks.allocationSamplingProbability != 1.0) { // Rather than generating a random number on every allocation to decide // if we want to sample that particular allocation (which would be // expensive), we calculate the number of allocations to skip before // taking the next sample. @@ -1195,22 +1195,20 @@ SavedStacksMetadataCallback(JSContext *c // greater than n is (~P)^n, as required. double notSamplingProb = 1.0 - stacks.allocationSamplingProbability; stacks.allocationSkipCount = std::floor(std::log(random_nextDouble(&stacks.rngState)) / std::log(notSamplingProb)); } RootedSavedFrame frame(cx); if (!stacks.saveCurrentStack(cx, &frame)) - CrashAtUnhandlableOOM("SavedStacksMetadataCallback"); + return false; + *pmetadata = frame; - if (!Debugger::onLogAllocationSite(cx, frame, PRMJ_Now())) - CrashAtUnhandlableOOM("SavedStacksMetadataCallback"); - - return frame; + return Debugger::onLogAllocationSite(cx, frame, PRMJ_Now()); } JS_FRIEND_API(JSPrincipals *) GetSavedFramePrincipals(HandleObject savedFrame) { MOZ_ASSERT(savedFrame); MOZ_ASSERT(savedFrame->is<SavedFrame>()); return savedFrame->as<SavedFrame>().getPrincipals();
--- a/js/src/vm/SavedStacks.h +++ b/js/src/vm/SavedStacks.h @@ -120,17 +120,17 @@ struct SavedFrame::HashPolicy static HashNumber hash(const Lookup &lookup); static bool match(SavedFrame *existing, const Lookup &lookup); typedef ReadBarriered<SavedFrame*> Key; static void rekey(Key &key, const Key &newKey); }; class SavedStacks { - friend JSObject *SavedStacksMetadataCallback(JSContext *cx); + friend bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); public: SavedStacks() : frames(), allocationSamplingProbability(1.0), allocationSkipCount(0), rngState(0) { } @@ -238,13 +238,13 @@ class SavedStacks { typedef HashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy> PCLocationMap; PCLocationMap pcLocationMap; void sweepPCLocationMap(); bool getLocation(JSContext *cx, const FrameIter &iter, MutableHandleLocationValue locationp); }; -JSObject *SavedStacksMetadataCallback(JSContext *cx); +bool SavedStacksMetadataCallback(JSContext *cx, JSObject **pmetadata); } /* namespace js */ #endif /* vm_SavedStacks_h */
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -11,17 +11,16 @@ #include "jscompartment.h" #include "jsiter.h" #include "vm/ArgumentsObject.h" #include "vm/GlobalObject.h" #include "vm/ProxyObject.h" #include "vm/Shape.h" -#include "vm/WeakMapObject.h" #include "vm/Xdr.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" #include "vm/Stack-inl.h" @@ -328,17 +327,18 @@ DeclEnvObject::createTemplateObject(JSCo MOZ_ASSERT(IsNurseryAllocable(FINALIZE_KIND)); RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedShape emptyDeclEnvShape(cx); emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE); + nullptr, FINALIZE_KIND, + BaseShape::DELEGATE); if (!emptyDeclEnvShape) return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, heap, emptyDeclEnvShape, group))); if (!obj) return nullptr; @@ -400,17 +400,17 @@ js::XDRStaticWithObject(XDRState<XDR_DEC StaticWithObject * StaticWithObject::create(ExclusiveContext *cx) { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND)); + nullptr, FINALIZE_KIND)); if (!shape) return nullptr; RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group)); if (!obj) return nullptr; return &obj->as<StaticWithObject>(); @@ -434,17 +434,17 @@ DynamicWithObject::create(JSContext *cx, { MOZ_ASSERT(staticWith->is<StaticWithObject>()); RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(staticWith.get()))); if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith), - FINALIZE_KIND)); + nullptr, FINALIZE_KIND)); if (!shape) return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group))); if (!obj) return nullptr; @@ -567,17 +567,18 @@ const Class DynamicWithObject::class_ = /* static */ StaticEvalObject * StaticEvalObject::create(JSContext *cx, HandleObject enclosing) { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE)); + nullptr, FINALIZE_KIND, + BaseShape::DELEGATE)); if (!shape) return nullptr; RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group))); if (!obj) return nullptr; @@ -666,17 +667,17 @@ StaticBlockObject::create(ExclusiveConte { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedShape emptyBlockShape(cx); emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, TaggedProto(nullptr), - FINALIZE_KIND, BaseShape::DELEGATE); + nullptr, FINALIZE_KIND, BaseShape::DELEGATE); if (!emptyBlockShape) return nullptr; JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, group); if (!obj) return nullptr; return &obj->as<StaticBlockObject>(); @@ -877,17 +878,17 @@ js::CloneNestedScopeObject(JSContext *cx /* static */ UninitializedLexicalObject * UninitializedLexicalObject::create(JSContext *cx, HandleObject enclosing) { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr))); if (!group) return nullptr; RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr), - FINALIZE_KIND)); + nullptr, FINALIZE_KIND)); if (!shape) return nullptr; RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group)); if (!obj) return nullptr; obj->as<ScopeObject>().setEnclosingScope(enclosing); @@ -1801,16 +1802,24 @@ js::IsDebugScopeSlow(ProxyObject *proxy) { MOZ_ASSERT(proxy->hasClass(&ProxyObject::class_)); return proxy->handler() == &DebugScopeProxy::singleton; } /*****************************************************************************/ /* 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) { // As above. Otherwise, barriers could fire during GC when moving the // value. typedef HashMap<ScopeObject *, MissingScopeKey, DefaultHasher<ScopeObject *>, RuntimeAllocPolicy> UnbarrieredLiveScopeMap; @@ -1823,22 +1832,29 @@ DebugScopes::DebugScopes(JSContext *cx) : proxiedScopes(cx), missingScopes(cx->runtime()), liveScopes(cx->runtime()) {} DebugScopes::~DebugScopes() { MOZ_ASSERT(missingScopes.empty()); + WeakMapBase::removeWeakMapFromList(&proxiedScopes); } bool DebugScopes::init() { - return liveScopes.init() && missingScopes.init(); + if (!liveScopes.init() || + !proxiedScopes.init() || + !missingScopes.init()) + { + return false; + } + return true; } void DebugScopes::mark(JSTracer *trc) { proxiedScopes.trace(trc); } @@ -1900,17 +1916,20 @@ DebugScopes::sweep(JSRuntime *rt) void DebugScopes::checkHashTablesAfterMovingGC(JSRuntime *runtime) { /* * This is called at the end of StoreBuffer::mark() to check that our * postbarriers have worked and that no hashtable keys (or values) are left * pointing into the nursery. */ - proxiedScopes.checkAfterMovingGC(); + for (ObjectWeakMap::Range r = proxiedScopes.all(); !r.empty(); r.popFront()) { + CheckGCThingAfterMovingGC(r.front().key().get()); + CheckGCThingAfterMovingGC(r.front().value().get()); + } for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) { CheckGCThingAfterMovingGC(r.front().key().staticScope()); CheckGCThingAfterMovingGC(r.front().value().get()); } for (LiveScopeMap::Range r = liveScopes.all(); !r.empty(); r.popFront()) { CheckGCThingAfterMovingGC(r.front().key()); CheckGCThingAfterMovingGC(r.front().value().staticScope_.get()); } @@ -1950,19 +1969,19 @@ DebugScopes::ensureCompartmentData(JSCon DebugScopeObject * DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope) { DebugScopes *scopes = scope.compartment()->debugScopes; if (!scopes) return nullptr; - if (JSObject *obj = scopes->proxiedScopes.lookup(&scope)) { + if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) { MOZ_ASSERT(CanUseDebugScopeMaps(cx)); - return &obj->as<DebugScopeObject>(); + return &p->value()->as<DebugScopeObject>(); } return nullptr; } bool DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope) { @@ -1971,17 +1990,24 @@ DebugScopes::addDebugScope(JSContext *cx if (!CanUseDebugScopeMaps(cx)) return true; DebugScopes *scopes = ensureCompartmentData(cx); if (!scopes) return false; - return scopes->proxiedScopes.add(cx, &scope, &debugScope); + MOZ_ASSERT(!scopes->proxiedScopes.has(&scope)); + if (!scopes->proxiedScopes.put(&scope, &debugScope)) { + ReportOutOfMemory(cx); + return false; + } + + proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope); + return true; } DebugScopeObject * DebugScopes::hasDebugScope(JSContext *cx, const ScopeIter &si) { MOZ_ASSERT(!si.hasScopeObject()); DebugScopes *scopes = cx->compartment()->debugScopes; @@ -2052,18 +2078,18 @@ DebugScopes::onPopCall(AbstractFramePtr if (!frame.hasCallObj()) return; if (frame.fun()->isGenerator()) return; CallObject &callobj = frame.scopeChain()->as<CallObject>(); scopes->liveScopes.remove(&callobj); - if (JSObject *obj = scopes->proxiedScopes.lookup(&callobj)) - debugScope = &obj->as<DebugScopeObject>(); + if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj)) + debugScope = &p->value()->as<DebugScopeObject>(); } else { ScopeIter si(cx, frame, frame.script()->main()); if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) { debugScope = p->value(); scopes->liveScopes.remove(&debugScope->scope().as<CallObject>()); scopes->missingScopes.remove(p); } }
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -9,17 +9,16 @@ #include "jscntxt.h" #include "jsobj.h" #include "jsweakmap.h" #include "gc/Barrier.h" #include "vm/ArgumentsObject.h" #include "vm/ProxyObject.h" -#include "vm/WeakMapObject.h" namespace js { namespace frontend { struct Definition; } class StaticWithObject; class StaticEvalObject; @@ -909,17 +908,20 @@ class DebugScopeObject : public ProxyObj // (and thus does not have a synthesized ScopeObject or a snapshot)? bool isOptimizedOut() const; }; /* Maintains per-compartment debug scope bookkeeping information. */ 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 * corresponding debug scopes. */ typedef HashMap<MissingScopeKey, ReadBarrieredDebugScopeObject, MissingScopeKey,
--- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -19,19 +19,21 @@ #include "vm/TypedArrayCommon.h" #include "jsatominlines.h" #include "jscntxtinlines.h" namespace js { inline -StackBaseShape::StackBaseShape(ExclusiveContext *cx, const Class *clasp, uint32_t objectFlags) +StackBaseShape::StackBaseShape(ExclusiveContext *cx, const Class *clasp, + JSObject *metadata, uint32_t objectFlags) : flags(objectFlags), clasp(clasp), + metadata(metadata), compartment(cx->compartment_) {} inline Shape * Shape::search(ExclusiveContext *cx, jsid id) { ShapeTable::Entry *_; return search(cx, this, id, &_);
--- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -318,17 +318,18 @@ ShapeTable::grow(ExclusiveContext *cx) Shape::replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base, TaggedProto proto, HandleShape shape) { MOZ_ASSERT(!shape->inDictionary()); 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, kind, + return EmptyShape::getInitialShape(cx, base.clasp, proto, + base.metadata, kind, base.flags & BaseShape::OBJECT_FLAG_MASK); } UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); if (!nbase) return nullptr; StackShape child(shape); @@ -618,17 +619,18 @@ js::ReshapeForAllocKind(JSContext *cx, S ids[nshape->slot()].set(nshape->propid()); nshape = nshape->previous(); } } // Construct the new shape, without updating type information. RootedId id(cx); RootedShape newShape(cx, EmptyShape::getInitialShape(cx, shape->getObjectClass(), - proto, nfixed, shape->getObjectFlags())); + proto, shape->getObjectMetadata(), + nfixed, shape->getObjectFlags())); for (unsigned i = 0; i < ids.length(); i++) { id = ids[i]; uint32_t index; bool indexed = IdIsIndex(id, &index); Rooted<UnownedBaseShape*> nbase(cx, newShape->base()->unowned()); if (indexed) { @@ -1123,16 +1125,55 @@ NativeObject::replaceWithNewEquivalentSh } bool 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) { if (hasAllFlags(flags)) return true; RootedObject self(cx, this); @@ -1192,70 +1233,162 @@ Shape::setObjectFlags(ExclusiveContext * return replaceLastProperty(cx, base, proto, lastRoot); } /* static */ inline HashNumber 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; + 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)); + } } /* static */ UnownedBaseShape* BaseShape::getUnowned(ExclusiveContext *cx, StackBaseShape &base) { BaseShapeSet &table = cx->compartment()->baseShapes; if (!table.initialized() && !table.init()) return nullptr; DependentAddPtr<BaseShapeSet> p(cx, table, base); if (p) return *p; + RootedGeneric<StackBaseShape*> root(cx, &base); + BaseShape *nbase_ = Allocate<BaseShape>(cx); if (!nbase_) return nullptr; new (nbase_) BaseShape(base); UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_); if (!p.add(cx, table, base, nbase)) return nullptr; + BaseShapesTablePostBarrier(cx, &table, nbase); + return nbase; } void 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() { if (!baseShapes.initialized()) return; 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()) { ReadBarriered<UnownedBaseShape *> b(base); e.rekeyFront(base, b); } } } @@ -1266,16 +1399,18 @@ void JSCompartment::checkBaseShapeTableAfterMovingGC() { if (!baseShapes.initialized()) return; 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()); } } #endif // JSGC_HASH_TABLE_CHECKS @@ -1297,83 +1432,95 @@ inline InitialShapeEntry::InitialShapeEntry(const ReadBarrieredShape &shape, TaggedProto proto) : shape(shape), proto(proto) { } inline InitialShapeEntry::Lookup InitialShapeEntry::getLookup() const { - return Lookup(shape->getObjectClass(), proto, shape->numFixedSlots(), shape->getObjectFlags()); + return Lookup(shape->getObjectClass(), proto, shape->getObjectMetadata(), + shape->numFixedSlots(), shape->getObjectFlags()); } /* static */ inline HashNumber 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; } /* static */ inline bool 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 objects which may be moved by generational GC. + * is calculated based on several 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; public: 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 (proto == priorProto) + if (metadata) + Mark(trc, &metadata, "initialShapes set metadata"); + if (proto == priorProto && metadata == priorMetadata) return; /* Find the original entry, which must still be present. */ - InitialShapeEntry::Lookup lookup(clasp, priorProto, nfixed, objectFlags); + InitialShapeEntry::Lookup lookup(clasp, priorProto, + priorMetadata, metadata, + nfixed, objectFlags); InitialShapeSet::Ptr p = set->lookup(lookup); MOZ_ASSERT(p); /* Update the entry's possibly-moved proto, and ensure lookup will still match. */ InitialShapeEntry &entry = const_cast<InitialShapeEntry&>(*p); entry.proto = proto; lookup.matchProto = proto; /* Rekey the entry. */ set->rekeyAs(lookup, - InitialShapeEntry::Lookup(clasp, proto, nfixed, objectFlags), + InitialShapeEntry::Lookup(clasp, proto, metadata, nfixed, objectFlags), *p); } }; #ifdef JSGC_HASH_TABLE_CHECKS void JSCompartment::checkInitialShapesTableAfterMovingGC() @@ -1388,19 +1535,22 @@ JSCompartment::checkInitialShapesTableAf */ for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) { InitialShapeEntry entry = e.front(); TaggedProto proto = entry.proto; Shape *shape = entry.shape.get(); 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); MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); } } #endif // JSGC_HASH_TABLE_CHECKS @@ -1415,62 +1565,66 @@ EmptyShape::new_(ExclusiveContext *cx, H } new (shape) EmptyShape(base, nfixed); return shape; } /* static */ Shape * EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto, - size_t nfixed, uint32_t objectFlags) + JSObject *metadata, size_t nfixed, uint32_t objectFlags) { MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); InitialShapeSet &table = cx->compartment()->initialShapes; if (!table.initialized() && !table.init()) return nullptr; typedef InitialShapeEntry::Lookup Lookup; DependentAddPtr<InitialShapeSet> - p(cx, table, Lookup(clasp, proto, nfixed, objectFlags)); + p(cx, table, Lookup(clasp, proto, metadata, nfixed, objectFlags)); if (p) return p->shape; Rooted<TaggedProto> protoRoot(cx, proto); + RootedObject metadataRoot(cx, metadata); - StackBaseShape base(cx, clasp, objectFlags); + StackBaseShape base(cx, clasp, metadata, objectFlags); Rooted<UnownedBaseShape*> nbase(cx, BaseShape::getUnowned(cx, base)); if (!nbase) return nullptr; Shape *shape = EmptyShape::new_(cx, nbase, nfixed); if (!shape) return nullptr; - Lookup lookup(clasp, protoRoot, nfixed, objectFlags); + Lookup lookup(clasp, protoRoot, metadataRoot, 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())) { - InitialShapeSetRef ref(&table, clasp, protoRoot, nfixed, objectFlags); + if ((protoRoot.isObject() && IsInsideNursery(protoRoot.toObject())) || + IsInsideNursery(metadataRoot.get())) + { + InitialShapeSetRef ref( + &table, clasp, protoRoot, metadataRoot, nfixed, objectFlags); cx->asJSContext()->runtime()->gc.storeBuffer.putGeneric(ref); } } return shape; } /* static */ Shape * EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto, - AllocKind kind, uint32_t objectFlags) + JSObject *metadata, AllocKind kind, uint32_t objectFlags) { - return getInitialShape(cx, clasp, proto, GetGCKindSlots(kind, clasp), objectFlags); + return getInitialShape(cx, clasp, proto, metadata, GetGCKindSlots(kind, clasp), objectFlags); } void NewObjectCache::invalidateEntriesForShape(JSContext *cx, HandleShape shape, HandleObject proto) { const Class *clasp = shape->getObjectClass(); gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots()); @@ -1488,16 +1642,17 @@ NewObjectCache::invalidateEntriesForShap if (lookupGroup(group, kind, &entry)) PodZero(&entries[entry]); } /* static */ void 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); MOZ_ASSERT(p); InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p); /* The new shape had better be rooted at the old one. */ @@ -1561,19 +1716,25 @@ JSCompartment::fixupInitialShapeTable() if (IsForwarded(entry.shape.get())) { entry.shape.set(Forwarded(entry.shape.get())); needRekey = true; } if (entry.proto.isObject() && IsForwarded(entry.proto.toObject())) { 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); } } } void
--- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -402,48 +402,73 @@ class BaseShape : public gc::TenuredCell // type had constructor information cleared. NEW_SCRIPT_CLEARED = 0x8000, OBJECT_FLAG_MASK = 0xfff8 }; 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 * dictionary last properties. */ /* For owned BaseShapes, the canonical unowned BaseShape. */ HeapPtrUnownedBaseShape unowned_; /* 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, uint32_t objectFlags) + BaseShape(JSCompartment *comp, const Class *clasp, JSObject *metadata, + 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; } explicit inline BaseShape(const StackBaseShape &base); /* Not defined: BaseShapes must not be stack allocated. */ ~BaseShape(); BaseShape &operator=(const BaseShape &other) { clasp_ = other.clasp_; + metadata = other.metadata; flags = other.flags; slotSpan_ = other.slotSpan_; compartment_ = other.compartment_; return *this; } const Class *clasp() const { return clasp_; } @@ -451,16 +476,17 @@ class BaseShape : public gc::TenuredCell inline void adoptUnowned(UnownedBaseShape *other); void setOwned(UnownedBaseShape *unowned) { flags |= OWNED_SHAPE; 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; } ShapeTable &table() const { MOZ_ASSERT(table_ && isOwned()); return *table_; } void setTable(ShapeTable *table) { MOZ_ASSERT(isOwned()); table_ = table; } uint32_t slotSpan() const { MOZ_ASSERT(isOwned()); return slotSpan_; } void setSlotSpan(uint32_t slotSpan) { MOZ_ASSERT(isOwned()); slotSpan_ = slotSpan; } @@ -488,16 +514,17 @@ class BaseShape : public gc::TenuredCell /* For JIT usage */ static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); } static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; } void markChildren(JSTracer *trc); void fixupAfterMovingGC() {} + bool fixupBaseShapeTableEntry(); private: static void staticAsserts() { JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_)); static_assert(sizeof(BaseShape) % gc::CellSize == 0, "Things inheriting from gc::Cell must have a size that's " "a multiple of gc::CellSize"); } @@ -541,52 +568,76 @@ BaseShape::baseUnowned() MOZ_ASSERT(isOwned() && unowned_); return unowned_; } /* Entries for the per-compartment baseShapes set of unowned base shapes. */ 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, uint32_t objectFlags); + inline StackBaseShape(ExclusiveContext *cx, const Class *clasp, + JSObject *metadata, 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) + : flags(base.flags), + clasp(base.clasp), + hashMetadata(base.metadata), + matchMetadata(base.metadata) {} MOZ_IMPLICIT Lookup(UnownedBaseShape *base) - : flags(base->getObjectFlags()), clasp(base->clasp()) + : flags(base->getObjectFlags()), + clasp(base->clasp()), + hashMetadata(base->getObjectMetadata()), + matchMetadata(base->getObjectMetadata()) { 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 BaseShape::BaseShape(const StackBaseShape &base) { mozilla::PodZero(this); this->clasp_ = base.clasp; + this->metadata = base.metadata; this->flags = base.flags; this->compartment_ = base.compartment; } typedef HashSet<ReadBarrieredUnownedBaseShape, StackBaseShape, SystemAllocPolicy> BaseShapeSet; @@ -751,17 +802,20 @@ class Shape : public gc::TenuredCell MOZ_ASSERT(!empty()); cursor = cursor->parent; } }; 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); uint32_t getObjectFlags() const { return base()->getObjectFlags(); } bool hasAllObjectFlags(BaseShape::Flag flags) const { MOZ_ASSERT(flags); MOZ_ASSERT(!(flags & ~BaseShape::OBJECT_FLAG_MASK)); return (base()->flags & flags) == flags; @@ -1053,16 +1107,17 @@ class AccessorShape : public Shape /* Get a shape identical to this one, without parent/kids information. */ inline AccessorShape(const StackShape &other, uint32_t nfixed); }; inline StackBaseShape::StackBaseShape(Shape *shape) : flags(shape->getObjectFlags()), clasp(shape->getObjectClass()), + metadata(shape->getObjectMetadata()), compartment(shape->compartment()) {} class AutoRooterGetterSetter { class Inner : private JS::CustomAutoRooter { public: @@ -1101,19 +1156,21 @@ struct EmptyShape : public js::Shape static Shape *new_(ExclusiveContext *cx, Handle<UnownedBaseShape *> base, uint32_t nfixed); /* * Lookup an initial shape matching the given parameters, creating an empty * shape if none was found. */ static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, - TaggedProto proto, size_t nfixed, uint32_t objectFlags = 0); + TaggedProto proto, JSObject *metadata, + size_t nfixed, uint32_t objectFlags = 0); static Shape *getInitialShape(ExclusiveContext *cx, const Class *clasp, - TaggedProto proto, gc::AllocKind kind, uint32_t objectFlags = 0); + TaggedProto proto, JSObject *metadata, + gc::AllocKind kind, uint32_t objectFlags = 0); /* * Reinsert an alternate initial shape, to be returned by future * getInitialShape calls, until the new shape becomes unreachable in a GC * and the table entry is purged. */ static void insertInitialShape(ExclusiveContext *cx, HandleShape shape, HandleObject proto); @@ -1150,22 +1207,39 @@ struct InitialShapeEntry */ TaggedProto proto; /* State used to determine a match on an initial shape. */ struct Lookup { const Class *clasp; TaggedProto hashProto; TaggedProto matchProto; + JSObject *hashMetadata; + JSObject *matchMetadata; uint32_t nfixed; uint32_t baseFlags; - - Lookup(const Class *clasp, TaggedProto proto, 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) + : clasp(clasp), + hashProto(proto), matchProto(proto), + hashMetadata(hashMetadata), matchMetadata(matchMetadata), nfixed(nfixed), baseFlags(baseFlags) {} }; inline InitialShapeEntry(); inline InitialShapeEntry(const ReadBarrieredShape &shape, TaggedProto proto); inline Lookup getLookup() const;
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3574,17 +3574,18 @@ TypeNewScript::maybeAnalyze(JSContext *c continue; PlainObject *obj = &objBase->as<PlainObject>(); // For now, we require all preliminary objects to have only simple // lineages of plain data properties. Shape *shape = obj->lastProperty(); if (shape->inDictionary() || !OnlyHasDataProperties(shape) || - shape->getObjectFlags() != 0) + shape->getObjectFlags() != 0 || + shape->getObjectMetadata() != nullptr) { return true; } maxSlotSpan = Max<size_t>(maxSlotSpan, obj->slotSpan()); if (prefixShape) { MOZ_ASSERT(shape->numFixedSlots() == prefixShape->numFixedSlots());
--- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -453,17 +453,18 @@ UnboxedLayout::makeNativeGroup(JSContext replacementNewGroup->setNewScript(replacementNewScript); gc::TraceTypeNewScript(replacementNewGroup); group->clearNewScript(cx, replacementNewGroup); } size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind()); - RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0)); + RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, + nullptr, nfixed, 0)); if (!shape) return false; for (size_t i = 0; i < layout.properties().length(); i++) { const UnboxedLayout::Property &property = layout.properties()[i]; StackShape unrootedChild(shape->base()->unowned(), NameToId(property.name), i, JSPROP_ENUMERATE, 0); @@ -1111,16 +1112,17 @@ js::TryConvertToUnboxedLayout(ExclusiveC // just constructed, so convert the existing group to be an // UnboxedPlainObject rather than a PlainObject, and update the preliminary // objects to use the new layout. Do the fallible stuff first before // modifying any objects. // 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(); return false; } // Accumulate a list of all the properties in each preliminary object, and // update their shapes.
--- a/js/src/vm/WeakMapObject.h +++ b/js/src/vm/WeakMapObject.h @@ -24,36 +24,11 @@ class ObjectValueMap : public WeakMap<Pr class WeakMapObject : public NativeObject { public: static const Class class_; 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 */
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2331,20 +2331,16 @@ ReportCompartmentStats(const JS::Compart ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("inner-views"), cStats.innerViewsTable, "The table for array buffer inner views."); ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"), 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."); ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"), cStats.regexpCompartment, "The regexp compartment and regexp data.");