author | Brian Hackett <bhackett1024@gmail.com> |
Fri, 06 Feb 2015 09:13:29 -0700 | |
changeset 227903 | 6bfcb81d3716bfcdcd0045d0fb80153159513a83 |
parent 227902 | e23d1b5c00d66f7d1d13ff64e4f751f67316fa9d |
child 227904 | 7d211cebe7fcd898be21cb7f11dfcbd4108bc20d |
push id | 28246 |
push user | kwierso@gmail.com |
push date | Fri, 06 Feb 2015 22:44:18 +0000 |
treeherder | autoland@f8ce4cf2e71d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1129226 |
milestone | 38.0a1 |
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/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -13,16 +13,17 @@ #include "builtin/Eval.h" #include "frontend/BytecodeCompiler.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" #include "vm/NativeObject-inl.h" +#include "vm/Shape-inl.h" using namespace js; using namespace js::types; using js::frontend::IsIdentifier; using mozilla::ArrayLength; using mozilla::UniquePtr; @@ -632,17 +633,17 @@ js::ObjectCreateImpl(JSContext *cx, Hand if (!proto) { // Object.create(null) is common, optimize it by using an allocation // site specific ObjectGroup. Because GetCallerInitGroup is pretty // slow, the caller can pass in the group if it's known and we use that // instead. RootedObjectGroup ngroup(cx, group); if (!ngroup) { - ngroup = GetCallerInitGroup(cx, JSProto_Null); + ngroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Null); if (!ngroup) return nullptr; } MOZ_ASSERT(!ngroup->proto().toObjectOrNull()); return NewObjectWithGroup<PlainObject>(cx, ngroup, cx->global(), allocKind, newKind);
--- a/js/src/builtin/Object.h +++ b/js/src/builtin/Object.h @@ -4,16 +4,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef builtin_Object_h #define builtin_Object_h #include "jsapi.h" +#include "vm/NativeObject.h" + namespace JS { class CallArgs; class Value; } namespace js { // Object constructor native. Exposed only so the JIT can know its address.
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1490,17 +1490,19 @@ OutlineTypedObject::createUnattachedWith const Class *clasp, HandleTypeDescr descr, int32_t length, gc::InitialHeap heap) { MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ || clasp == &OutlineOpaqueTypedObject::class_); - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr)); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, + TaggedProto(&descr->typedProto()), + descr)); if (!group) return nullptr; NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject; OutlineTypedObject *obj = NewObjectWithGroup<OutlineTypedObject>(cx, group, cx->global(), gc::FINALIZE_OBJECT0, newKind); if (!obj) return nullptr; @@ -2126,17 +2128,19 @@ OutlineTypedObject::neuter(void *newData InlineTypedObject::create(JSContext *cx, HandleTypeDescr descr, gc::InitialHeap heap) { gc::AllocKind allocKind = allocKindForTypeDescriptor(descr); const Class *clasp = descr->opaque() ? &InlineOpaqueTypedObject::class_ : &InlineTransparentTypedObject::class_; - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(&descr->typedProto()), descr)); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, + TaggedProto(&descr->typedProto()), + descr)); if (!group) return nullptr; NewObjectKind newKind = (heap == gc::TenuredHeap) ? MaybeSingletonObject : GenericObject; return NewObjectWithGroup<InlineTypedObject>(cx, group, cx->global(), allocKind, newKind); } /* static */ InlineTypedObject *
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4290,17 +4290,17 @@ ParseNode::getConstantValue(ExclusiveCon return true; } id = INT_TO_JSID(idx); if (!DefineProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE)) return false; } MOZ_ASSERT(idx == count); - types::FixArrayGroup(cx, obj); + ObjectGroup::fixArrayGroup(cx, obj); vp.setObject(*obj); return true; } case PNK_OBJECT: { MOZ_ASSERT(isOp(JSOP_NEWINIT)); MOZ_ASSERT(!(pn_xflags & PNX_NONCONST)); if (allowObjects == DontAllowObjects) { @@ -4353,17 +4353,17 @@ ParseNode::getConstantValue(ExclusiveCon if (!DefineProperty(cx, obj, name->asPropertyName(), value, nullptr, nullptr, JSPROP_ENUMERATE)) { return false; } } } - types::FixObjectGroup(cx, obj); + ObjectGroup::fixPlainObjectGroup(cx, obj); vp.setObject(*obj); return true; } default: MOZ_CRASH("Unexpected node"); } return false; } @@ -7245,23 +7245,23 @@ frontend::EmitTree(ExclusiveContext *cx, // If the array consists entirely of primitive values, make a // template object with copy on write elements that can be reused // every time the initializer executes. if (bce->emitterMode != BytecodeEmitter::SelfHosting && pn->pn_count != 0) { RootedValue value(cx); if (!pn->getConstantValue(cx, ParseNode::DontAllowNestedObjects, &value)) return false; if (!value.isMagic(JS_GENERIC_MAGIC)) { - // Note: the type of the template object might not yet reflect + // Note: the group of the template object might not yet reflect // that the object has copy on write elements. When the // interpreter or JIT compiler fetches the template, it should - // use types::GetOrFixupCopyOnWriteObject to make sure the type - // for the template is accurate. We don't do this here as we - // want to use types::InitObject, which requires a finished - // script. + // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the + // group for the template is accurate. We don't do this here as we + // want to use ObjectGroup::allocationSiteGroup, which requires a + // finished script. NativeObject *obj = &value.toObject().as<NativeObject>(); if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj)) return false; ObjectBox *objbox = bce->parser->newObjectBox(obj); if (!objbox) return false;
--- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -176,20 +176,17 @@ class NestedScopeObject; class Nursery; class PlainObject; class PropertyName; class SavedFrame; class ScopeObject; class ScriptSourceObject; class Shape; class UnownedBaseShape; - -namespace types { -struct ObjectGroup; -} +class ObjectGroup; namespace jit { class JitCode; } #ifdef DEBUG // Barriers can't be triggered during backend Ion compilation, which may run on // a helper thread. @@ -227,17 +224,17 @@ template <> struct MapTypeToTraceKind<Pr template <> struct MapTypeToTraceKind<SavedFrame> { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind<ScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind<Shape> { static const JSGCTraceKind kind = JSTRACE_SHAPE; }; template <> struct MapTypeToTraceKind<AccessorShape> { static const JSGCTraceKind kind = JSTRACE_SHAPE; }; template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind<SharedTypedArrayObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; }; template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; template <> struct MapTypeToTraceKind<jit::JitCode> { static const JSGCTraceKind kind = JSTRACE_JITCODE; }; -template <> struct MapTypeToTraceKind<types::ObjectGroup> { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; +template <> struct MapTypeToTraceKind<ObjectGroup> { static const JSGCTraceKind kind = JSTRACE_OBJECT_GROUP; }; // Direct value access used by the write barriers and the jits. void MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name); // These three declarations are also present in gc/Marking.h, via the DeclMarker // macro. Not great, but hard to avoid. void @@ -801,17 +798,17 @@ typedef HeapPtr<JSLinearString*> HeapPtr typedef HeapPtr<JSObject*> HeapPtrObject; typedef HeapPtr<JSScript*> HeapPtrScript; typedef HeapPtr<JSString*> HeapPtrString; typedef HeapPtr<PlainObject*> HeapPtrPlainObject; typedef HeapPtr<PropertyName*> HeapPtrPropertyName; typedef HeapPtr<Shape*> HeapPtrShape; typedef HeapPtr<UnownedBaseShape*> HeapPtrUnownedBaseShape; typedef HeapPtr<jit::JitCode*> HeapPtrJitCode; -typedef HeapPtr<types::ObjectGroup*> HeapPtrObjectGroup; +typedef HeapPtr<ObjectGroup*> HeapPtrObjectGroup; typedef PreBarriered<Value> PreBarrieredValue; typedef RelocatablePtr<Value> RelocatableValue; typedef HeapPtr<Value> HeapValue; typedef PreBarriered<jsid> PreBarrieredId; typedef RelocatablePtr<jsid> RelocatableId; typedef HeapPtr<jsid> HeapId; @@ -822,17 +819,17 @@ typedef ImmutableTenuredPtr<JS::Symbol*> typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject; typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject; typedef ReadBarriered<JSFunction*> ReadBarrieredFunction; typedef ReadBarriered<JSObject*> ReadBarrieredObject; typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject; typedef ReadBarriered<Shape*> ReadBarrieredShape; typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape; typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode; -typedef ReadBarriered<types::ObjectGroup*> ReadBarrieredObjectGroup; +typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup; typedef ReadBarriered<JSAtom*> ReadBarrieredAtom; typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol; typedef ReadBarriered<Value> ReadBarrieredValue; // A pre- and post-barriered Value that is specialized to be aware that it // resides in a slots or elements vector. This allows it to be relocated in // memory, but with substantially less overhead than a RelocatablePtr.
--- a/js/src/gc/GCTrace.h +++ b/js/src/gc/GCTrace.h @@ -6,17 +6,17 @@ #ifndef gc_GCTrace_h #define gc_GCTrace_h #include "gc/Heap.h" namespace js { -namespace types { struct ObjectGroup; } +class ObjectGroup; namespace gc { #ifdef JS_GC_TRACE extern bool InitTrace(GCRuntime &gc); extern void FinishTrace(); extern bool TraceEnabled(); @@ -24,32 +24,32 @@ extern void TraceNurseryAlloc(Cell *thin extern void TraceTenuredAlloc(Cell *thing, AllocKind kind); extern void TraceCreateObject(JSObject* object); extern void TraceMinorGCStart(); extern void TracePromoteToTenured(Cell *src, Cell *dst); extern void TraceMinorGCEnd(); extern void TraceMajorGCStart(); extern void TraceTenuredFinalize(Cell *thing); extern void TraceMajorGCEnd(); -extern void TraceTypeNewScript(js::types::ObjectGroup *group); +extern void TraceTypeNewScript(js::ObjectGroup *group); #else inline bool InitTrace(GCRuntime &gc) { return true; } inline void FinishTrace() {} inline bool TraceEnabled() { return false; } inline void TraceNurseryAlloc(Cell *thing, size_t size) {} inline void TraceTenuredAlloc(Cell *thing, AllocKind kind) {} inline void TraceCreateObject(JSObject* object) {} inline void TraceMinorGCStart() {} inline void TracePromoteToTenured(Cell *src, Cell *dst) {} inline void TraceMinorGCEnd() {} inline void TraceMajorGCStart() {} inline void TraceTenuredFinalize(Cell *thing) {} inline void TraceMajorGCEnd() {} -inline void TraceTypeNewScript(js::types::ObjectGroup *group) {} +inline void TraceTypeNewScript(js::ObjectGroup *group) {} #endif } /* namespace gc */ } /* namespace js */ #endif
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -82,28 +82,28 @@ PushMarkStack(GCMarker *gcmarker, Shape static inline void PushMarkStack(GCMarker *gcmarker, JSString *str); static inline void PushMarkStack(GCMarker *gcmarker, JS::Symbol *sym); static inline void -PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing); +PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing); namespace js { namespace gc { static void MarkChildren(JSTracer *trc, JSString *str); static void MarkChildren(JSTracer *trc, JS::Symbol *sym); static void MarkChildren(JSTracer *trc, JSScript *script); static void MarkChildren(JSTracer *trc, LazyScript *lazy); static void MarkChildren(JSTracer *trc, Shape *shape); static void MarkChildren(JSTracer *trc, BaseShape *base); -static void MarkChildren(JSTracer *trc, types::ObjectGroup *group); +static void MarkChildren(JSTracer *trc, ObjectGroup *group); static void MarkChildren(JSTracer *trc, jit::JitCode *code); } /* namespace gc */ } /* namespace js */ /*** Object Marking ***/ #if defined(DEBUG) @@ -609,17 +609,17 @@ DeclMarkerImpl(Script, JSScript) DeclMarkerImpl(LazyScript, LazyScript) DeclMarkerImpl(Shape, Shape) DeclMarkerImpl(String, JSAtom) DeclMarkerImpl(String, JSString) DeclMarkerImpl(String, JSFlatString) DeclMarkerImpl(String, JSLinearString) DeclMarkerImpl(String, PropertyName) DeclMarkerImpl(Symbol, JS::Symbol) -DeclMarkerImpl(ObjectGroup, js::types::ObjectGroup) +DeclMarkerImpl(ObjectGroup, js::ObjectGroup) } /* namespace gc */ } /* namespace js */ /*** Externally Typed Marking ***/ void gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind) @@ -650,17 +650,17 @@ gc::MarkKind(JSTracer *trc, void **thing break; case JSTRACE_LAZY_SCRIPT: MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp)); break; case JSTRACE_SHAPE: MarkInternal(trc, reinterpret_cast<Shape **>(thingp)); break; case JSTRACE_OBJECT_GROUP: - MarkInternal(trc, reinterpret_cast<types::ObjectGroup **>(thingp)); + MarkInternal(trc, reinterpret_cast<ObjectGroup **>(thingp)); break; default: MOZ_CRASH("Invalid trace kind in MarkKind."); } } static void MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name) @@ -791,17 +791,17 @@ gc::MarkTypeRoot(JSTracer *trc, types::T { JS_ROOT_MARKING_ASSERT(trc); trc->setTracingName(name); if (v->isSingleton()) { JSObject *obj = v->singleton(); MarkInternal(trc, &obj); *v = types::Type::ObjectType(obj); } else if (v->isGroup()) { - types::ObjectGroup *group = v->group(); + ObjectGroup *group = v->group(); MarkInternal(trc, &group); *v = types::Type::ObjectType(group); } } void gc::MarkValueRange(JSTracer *trc, size_t len, BarrieredBase<Value> *vec, const char *name) { @@ -1061,17 +1061,17 @@ PushMarkStack(GCMarker *gcmarker, JSFunc JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing); MOZ_ASSERT(!IsInsideNursery(thing)); if (thing->asTenured().markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushObject(thing); } static void -PushMarkStack(GCMarker *gcmarker, types::ObjectGroup *thing) +PushMarkStack(GCMarker *gcmarker, ObjectGroup *thing) { JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing); MOZ_ASSERT(!IsInsideNursery(thing)); if (thing->markIfUnmarked(gcmarker->getMarkColor())) gcmarker->pushType(thing); } @@ -1417,17 +1417,17 @@ gc::MarkCycleCollectorChildren(JSTracer MOZ_ASSERT(tmp == shape->setterObject()); } shape = shape->previous(); } while (shape); } static void -ScanObjectGroup(GCMarker *gcmarker, types::ObjectGroup *group) +ScanObjectGroup(GCMarker *gcmarker, ObjectGroup *group) { unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { if (types::Property *prop = group->getProperty(i)) MarkId(gcmarker, &prop->id, "ObjectGroup property id"); } if (group->proto().isObject()) @@ -1445,17 +1445,17 @@ ScanObjectGroup(GCMarker *gcmarker, type if (TypeDescr *descr = group->maybeTypeDescr()) PushMarkStack(gcmarker, descr); if (JSFunction *fun = group->maybeInterpretedFunction()) PushMarkStack(gcmarker, fun); } static void -gc::MarkChildren(JSTracer *trc, types::ObjectGroup *group) +gc::MarkChildren(JSTracer *trc, ObjectGroup *group) { unsigned count = group->getPropertyCount(); for (unsigned i = 0; i < count; i++) { types::Property *prop = group->getProperty(i); if (prop) MarkId(trc, &prop->id, "group_property"); } @@ -1528,17 +1528,17 @@ gc::PushArena(GCMarker *gcmarker, ArenaH PushArenaTyped<LazyScript>(gcmarker, aheader); break; case JSTRACE_SHAPE: PushArenaTyped<js::Shape>(gcmarker, aheader); break; case JSTRACE_OBJECT_GROUP: - PushArenaTyped<js::types::ObjectGroup>(gcmarker, aheader); + PushArenaTyped<js::ObjectGroup>(gcmarker, aheader); break; default: MOZ_CRASH("Invalid trace kind in PushArena."); } } struct SlotArrayLayout @@ -1647,17 +1647,17 @@ GCMarker::restoreValueArray(NativeObject MOZ_ASSERT(*vpp <= *endp); return true; } void GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr) { if (tag == GroupTag) { - ScanObjectGroup(this, reinterpret_cast<types::ObjectGroup *>(addr)); + ScanObjectGroup(this, reinterpret_cast<ObjectGroup *>(addr)); } else if (tag == SavedValueArrayTag) { MOZ_ASSERT(!(addr & CellMask)); NativeObject *obj = reinterpret_cast<NativeObject *>(addr); HeapValue *vp, *end; if (restoreValueArray(obj, (void **)&vp, (void **)&end)) pushValueArray(obj, vp, end); else pushObject(obj); @@ -1797,17 +1797,17 @@ GCMarker::processMarkStackTop(SliceBudge JS_COMPARTMENT_ASSERT(runtime(), obj); budget.step(); if (budget.isOverBudget()) { pushObject(obj); return; } - types::ObjectGroup *group = obj->groupFromGC(); + ObjectGroup *group = obj->groupFromGC(); PushMarkStack(this, group); Shape *shape = obj->lastProperty(); PushMarkStack(this, shape); /* Call the trace hook if necessary. */ const Class *clasp = group->clasp(); if (clasp->trace) { @@ -1952,17 +1952,17 @@ js::TraceChildren(JSTracer *trc, void *t MarkChildren(trc, static_cast<LazyScript *>(thing)); break; case JSTRACE_SHAPE: MarkChildren(trc, static_cast<Shape *>(thing)); break; case JSTRACE_OBJECT_GROUP: - MarkChildren(trc, (types::ObjectGroup *)thing); + MarkChildren(trc, (ObjectGroup *)thing); break; default: MOZ_CRASH("Invalid trace kind in TraceChildren."); } } #ifdef DEBUG
--- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -127,17 +127,17 @@ DeclMarker(Script, JSScript) DeclMarker(LazyScript, LazyScript) DeclMarker(Shape, Shape) DeclMarker(String, JSAtom) DeclMarker(String, JSString) DeclMarker(String, JSFlatString) DeclMarker(String, JSLinearString) DeclMarker(String, PropertyName) DeclMarker(Symbol, JS::Symbol) -DeclMarker(ObjectGroup, types::ObjectGroup) +DeclMarker(ObjectGroup, ObjectGroup) #undef DeclMarker void MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name); void MarkWellKnownSymbol(JSTracer *trc, JS::Symbol *sym);
--- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -532,31 +532,31 @@ js::Nursery::forwardBufferPointer(HeapSl MOZ_ASSERT(!isInside(*pSlotsElems)); MOZ_ASSERT(IsWriteableAddress(*pSlotsElems)); } // Structure for counting how many times objects in a particular group have // been tenured during a minor collection. struct TenureCount { - types::ObjectGroup *group; + ObjectGroup *group; int count; }; // Keep rough track of how many times we tenure objects in particular groups // during minor collections, using a fixed size hash for efficiency at the cost // of potential collisions. struct Nursery::TenureCountCache { TenureCount entries[16]; TenureCountCache() { PodZero(this); } - TenureCount &findEntry(types::ObjectGroup *group) { - return entries[PointerHasher<types::ObjectGroup *, 3>::hash(group) % ArrayLength(entries)]; + TenureCount &findEntry(ObjectGroup *group) { + return entries[PointerHasher<ObjectGroup *, 3>::hash(group) % ArrayLength(entries)]; } }; void js::Nursery::collectToFixedPoint(MinorCollectionTracer *trc, TenureCountCache &tenureCounts) { for (RelocationOverlay *p = trc->head; p; p = p->next()) { JSObject *obj = static_cast<JSObject*>(p->forwardingAddress());
--- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -26,28 +26,26 @@ struct Zone; } namespace js { class TypedArrayObject; class ObjectElements; class NativeObject; class HeapSlot; +class ObjectGroup; + void SetGCZeal(JSRuntime *, uint8_t, uint32_t); namespace gc { struct Cell; class Collector; class MinorCollectionTracer; } /* namespace gc */ -namespace types { -struct ObjectGroup; -} - namespace jit { class CodeGenerator; class MacroAssembler; class ICStubCompiler; class BaselineCompiler; } class Nursery @@ -112,17 +110,17 @@ class Nursery /* Resize an existing elements vector. */ ObjectElements *reallocateElements(JSObject *obj, ObjectElements *oldHeader, uint32_t oldCount, uint32_t newCount); /* Free a slots array. */ void freeSlots(HeapSlot *slots); - typedef Vector<types::ObjectGroup *, 0, SystemAllocPolicy> ObjectGroupList; + typedef Vector<ObjectGroup *, 0, SystemAllocPolicy> ObjectGroupList; /* * Do a minor collection, optionally specifying a list to store groups which * should be pretenured afterwards. */ void collect(JSRuntime *rt, JS::gcreason::Reason reason, ObjectGroupList *pretenureGroups); /*
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -98,17 +98,17 @@ MarkExactStackRootList(JSTracer *trc, So template<class T> static void MarkExactStackRootsAcrossTypes(T context, JSTracer *trc) { MarkExactStackRootList<JSObject *, MarkObjectRoot>(trc, context, "exact-object"); MarkExactStackRootList<Shape *, MarkShapeRoot>(trc, context, "exact-shape"); MarkExactStackRootList<BaseShape *, MarkBaseShapeRoot>(trc, context, "exact-baseshape"); - MarkExactStackRootList<types::ObjectGroup *, MarkObjectGroupRoot>( + MarkExactStackRootList<ObjectGroup *, MarkObjectGroupRoot>( trc, context, "exact-objectgroup"); MarkExactStackRootList<JSString *, MarkStringRoot>(trc, context, "exact-string"); MarkExactStackRootList<JS::Symbol *, MarkSymbolRoot>(trc, context, "exact-symbol"); MarkExactStackRootList<jit::JitCode *, MarkJitCodeRoot>(trc, context, "exact-jitcode"); MarkExactStackRootList<JSScript *, MarkScriptRoot>(trc, context, "exact-script"); MarkExactStackRootList<LazyScript *, MarkLazyScriptRoot>(trc, context, "exact-lazy-script"); MarkExactStackRootList<jsid, MarkIdRoot>(trc, context, "exact-id"); MarkExactStackRootList<Value, MarkValueRoot>(trc, context, "exact-value");
--- a/js/src/gc/Rooting.h +++ b/js/src/gc/Rooting.h @@ -15,38 +15,37 @@ class JSLinearString; namespace js { class PropertyName; class NativeObject; class ArrayObject; class PlainObject; class ScriptSourceObject; class Shape; - -namespace types { struct ObjectGroup; } +class ObjectGroup; // These are internal counterparts to the public types such as HandleObject. typedef JS::Handle<NativeObject*> HandleNativeObject; typedef JS::Handle<Shape*> HandleShape; -typedef JS::Handle<types::ObjectGroup*> HandleObjectGroup; +typedef JS::Handle<ObjectGroup*> HandleObjectGroup; typedef JS::Handle<JSAtom*> HandleAtom; typedef JS::Handle<JSLinearString*> HandleLinearString; typedef JS::Handle<PropertyName*> HandlePropertyName; typedef JS::Handle<ArrayObject*> HandleArrayObject; typedef JS::Handle<PlainObject*> HandlePlainObject; typedef JS::Handle<ScriptSourceObject*> HandleScriptSource; typedef JS::MutableHandle<Shape*> MutableHandleShape; typedef JS::MutableHandle<JSAtom*> MutableHandleAtom; typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject; typedef JS::Rooted<NativeObject*> RootedNativeObject; typedef JS::Rooted<Shape*> RootedShape; -typedef JS::Rooted<types::ObjectGroup*> RootedObjectGroup; +typedef JS::Rooted<ObjectGroup*> RootedObjectGroup; typedef JS::Rooted<JSAtom*> RootedAtom; typedef JS::Rooted<JSLinearString*> RootedLinearString; typedef JS::Rooted<PropertyName*> RootedPropertyName; typedef JS::Rooted<ArrayObject*> RootedArrayObject; typedef JS::Rooted<PlainObject*> RootedPlainObject; typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource; } /* namespace js */
--- a/js/src/gc/Tracer.h +++ b/js/src/gc/Tracer.h @@ -11,25 +11,23 @@ #include "js/GCAPI.h" #include "js/SliceBudget.h" #include "js/TracingAPI.h" namespace js { class NativeObject; class GCMarker; +class ObjectGroup; namespace gc { struct ArenaHeader; } namespace jit { class JitCode; } -namespace types { -struct ObjectGroup; -} static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096; static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768; /* * When the native stack is low, the GC does not call JS_TraceChildren to mark * the reachable "children" of the thing. Rather the thing is put aside and * JS_TraceChildren is called later with more space on the C stack. @@ -138,17 +136,17 @@ class GCMarker : public JSTracer void start(); void stop(); void reset(); void pushObject(JSObject *obj) { pushTaggedPtr(ObjectTag, obj); } - void pushType(types::ObjectGroup *group) { + void pushType(ObjectGroup *group) { pushTaggedPtr(GroupTag, group); } void pushJitCode(jit::JitCode *code) { pushTaggedPtr(JitCodeTag, code); } uint32_t getMarkColor() const {
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1717,18 +1717,18 @@ BaselineCompiler::emit_JSOP_LINENO() bool BaselineCompiler::emit_JSOP_NEWARRAY() { frame.syncStack(0); uint32_t length = GET_UINT24(pc); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, JSProto_Array)) { - group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Array)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; } // Pass length in R0, group in R1. masm.move32(Imm32(length), R0.scratchReg()); masm.movePtr(ImmGCPtr(group), R1.scratchReg()); @@ -1748,17 +1748,17 @@ BaselineCompiler::emit_JSOP_NEWARRAY() typedef JSObject *(*NewArrayCopyOnWriteFn)(JSContext *, HandleArrayObject, gc::InitialHeap); const VMFunction jit::NewArrayCopyOnWriteInfo = FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray); bool BaselineCompiler::emit_JSOP_NEWARRAY_COPYONWRITE() { RootedScript scriptRoot(cx, script); - JSObject *obj = types::GetOrFixupCopyOnWriteObject(cx, scriptRoot, pc); + JSObject *obj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, scriptRoot, pc); if (!obj) return false; prepareVMCall(); pushArg(Imm32(gc::DefaultHeap)); pushArg(ImmGCPtr(obj)); @@ -1792,18 +1792,18 @@ BaselineCompiler::emit_JSOP_INITELEM_ARR } bool BaselineCompiler::emit_JSOP_NEWOBJECT() { frame.syncStack(0); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, JSProto_Object)) { - group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Object); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, JSProto_Object)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Object); if (!group) return false; } RootedPlainObject baseObject(cx, &script->getObject(pc)->as<PlainObject>()); RootedPlainObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject)); if (!templateObject) return false; @@ -1817,18 +1817,18 @@ BaselineCompiler::emit_JSOP_NEWOBJECT() // Try to do the allocation inline. Label done; if (group && !group->shouldPreTenure() && !templateObject->hasDynamicSlots()) { Label slowPath; Register objReg = R0.scratchReg(); Register tempReg = R1.scratchReg(); masm.movePtr(ImmGCPtr(group), tempReg); - masm.branchTest32(Assembler::NonZero, Address(tempReg, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_PRE_TENURE), &slowPath); + masm.branchTest32(Assembler::NonZero, Address(tempReg, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_PRE_TENURE), &slowPath); masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->compartment()->addressOfMetadataCallback()), ImmWord(0), &slowPath); masm.createGCObject(objReg, tempReg, templateObject, gc::DefaultHeap, &slowPath); masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0); masm.jump(&done); masm.bind(&slowPath); } @@ -1843,18 +1843,18 @@ BaselineCompiler::emit_JSOP_NEWOBJECT() bool BaselineCompiler::emit_JSOP_NEWINIT() { frame.syncStack(0); JSProtoKey key = JSProtoKey(GET_UINT8(pc)); RootedObjectGroup group(cx); - if (!types::UseSingletonForInitializer(script, pc, key)) { - group = types::TypeScript::InitGroup(cx, script, pc, key); + if (!ObjectGroup::useSingletonForAllocationSite(script, pc, key)) { + group = ObjectGroup::allocationSiteGroup(cx, script, pc, key); if (!group) return false; } if (key == JSProto_Array) { // Pass length in R0, group in R1. masm.move32(Imm32(0), R0.scratchReg()); masm.movePtr(ImmGCPtr(group), R1.scratchReg()); @@ -3359,17 +3359,17 @@ BaselineCompiler::emit_JSOP_RUNONCE() bool BaselineCompiler::emit_JSOP_REST() { frame.syncStack(0); ArrayObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); if (!templateObject) return false; - types::FixRestArgumentsType(cx, templateObject); + ObjectGroup::fixRestArgumentsGroup(cx, templateObject); // Call IC. ICRest_Fallback::Compiler compiler(cx, templateObject); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; // Mark R0 as pushed stack value. frame.push(R0);
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -2057,17 +2057,17 @@ ICCompare_ObjectWithUndefined::Compiler: // obj !== undefined for all objects. masm.moveValue(BooleanValue(op == JSOP_STRICTNE), R0); EmitReturnFromIC(masm); } else { // obj != undefined only where !obj->getClass()->emulatesUndefined() Label emulatesUndefined; Register obj = masm.extractObject(objectOperand, ExtractTemp0); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), obj); - masm.loadPtr(Address(obj, types::ObjectGroup::offsetOfClasp()), obj); + masm.loadPtr(Address(obj, ObjectGroup::offsetOfClasp()), obj); masm.branchTest32(Assembler::NonZero, Address(obj, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED), &emulatesUndefined); masm.moveValue(BooleanValue(op == JSOP_NE), R0); EmitReturnFromIC(masm); masm.bind(&emulatesUndefined); masm.moveValue(BooleanValue(op == JSOP_EQ), R0); @@ -4516,17 +4516,17 @@ LoadTypedThingLength(MacroAssembler &mas { switch (layout) { case Layout_TypedArray: masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), result); break; case Layout_OutlineTypedObject: case Layout_InlineTypedObject: masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), result); - masm.loadPtr(Address(result, types::ObjectGroup::offsetOfAddendum()), result); + masm.loadPtr(Address(result, ObjectGroup::offsetOfAddendum()), result); masm.unboxInt32(Address(result, ArrayTypeDescr::offsetOfLength()), result); break; default: MOZ_CRASH(); } } static void @@ -8515,17 +8515,17 @@ ICSetPropNativeAddCompiler::generateStub // Check if the cache has a new group to change to. masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); masm.branchTestPtr(Assembler::Zero, scratch, scratch, &noGroupChange); // Check if the old group still has a newScript. masm.loadPtr(Address(objReg, JSObject::offsetOfGroup()), scratch); masm.branchPtr(Assembler::Equal, - Address(scratch, types::ObjectGroup::offsetOfAddendum()), + Address(scratch, ObjectGroup::offsetOfAddendum()), ImmWord(0), &noGroupChange); // Reload the new group from the cache. masm.loadPtr(Address(BaselineStubReg, ICSetProp_NativeAdd::offsetOfNewGroup()), scratch); // Change the object's group. Address groupAddr(objReg, JSObject::offsetOfGroup()); @@ -9044,29 +9044,29 @@ GetTemplateObjectForNative(JSContext *cx if (args.length() != 1) count = args.length(); else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0) count = args[0].toInt32(); res.set(NewDenseUnallocatedArray(cx, count, nullptr, TenuredObject)); if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); return true; } if (native == intrinsic_NewDenseArray) { res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject)); if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); return true; } if (native == js::array_concat) { if (args.thisv().isObject() && args.thisv().toObject().is<ArrayObject>() && @@ -9080,17 +9080,17 @@ GetTemplateObjectForNative(JSContext *cx } } if (native == js::str_split && args.length() == 1 && args[0].isString()) { res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject)); if (!res) return false; - types::ObjectGroup *group = types::TypeScript::InitGroup(cx, script, pc, JSProto_Array); + ObjectGroup *group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array); if (!group) return false; res->setGroup(group); return true; } if (native == js_String) { RootedString emptyString(cx, cx->runtime()->emptyString); @@ -9482,17 +9482,17 @@ DoCallFallback(JSContext *cx, BaselineFr if (op == JSOP_FUNAPPLY && argc == 2 && args[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) { CallArgs callArgs = CallArgsFromVp(argc, vp); if (!GuardFunApplyArgumentsOptimization(cx, frame, callArgs)) return false; } // Compute construcing and useNewGroup flags. bool constructing = (op == JSOP_NEW); - bool createSingleton = types::UseSingletonForNewObject(cx, script, pc); + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc); // Try attaching a call stub. if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false, createSingleton)) return false; if (!MaybeCloneFunctionAtCallsite(cx, &callee, script, pc)) return false; @@ -11604,17 +11604,17 @@ ICGetElem_Arguments::Clone(JSContext *, } ICSetElem_Dense::ICSetElem_Dense(JitCode *stubCode, HandleShape shape, HandleObjectGroup group) : ICUpdatedStub(SetElem_Dense, stubCode), shape_(shape), group_(group) { } -ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, +ICSetElem_DenseAdd::ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth) : ICUpdatedStub(SetElem_DenseAdd, stubCode), group_(group) { MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH); extra_ = protoChainDepth; } @@ -12118,17 +12118,17 @@ static bool DoRestFallback(JSContext *cx unsigned numFormals = frame->numFormalArgs() - 1; unsigned numActuals = frame->numActualArgs(); unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; Value *rest = frame->argv() + numFormals; ArrayObject *obj = NewDenseCopiedArray(cx, numRest, rest, nullptr); if (!obj) return false; - types::FixRestArgumentsType(cx, obj); + ObjectGroup::fixRestArgumentsGroup(cx, obj); res.setObject(*obj); return true; } typedef bool (*DoRestFallbackFn)(JSContext *, ICRest_Fallback *, BaselineFrame *, MutableHandleValue); static const VMFunction DoRestFallbackInfo = FunctionInfo<DoRestFallbackFn>(DoRestFallback, TailCall);
--- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -3571,17 +3571,17 @@ class ICSetElem_DenseAdd : public ICUpda friend class ICStubSpace; public: static const size_t MAX_PROTO_CHAIN_DEPTH = 4; protected: HeapPtrObjectGroup group_; - ICSetElem_DenseAdd(JitCode *stubCode, types::ObjectGroup *group, size_t protoChainDepth); + ICSetElem_DenseAdd(JitCode *stubCode, ObjectGroup *group, size_t protoChainDepth); public: static size_t offsetOfGroup() { return offsetof(ICSetElem_DenseAdd, group_); } HeapPtrObjectGroup &group() { return group_; @@ -3606,28 +3606,28 @@ class ICSetElem_DenseAdd : public ICUpda template <size_t ProtoChainDepth> class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd { friend class ICStubSpace; static const size_t NumShapes = ProtoChainDepth + 1; mozilla::Array<HeapPtrShape, NumShapes> shapes_; - ICSetElem_DenseAddImpl(JitCode *stubCode, types::ObjectGroup *group, + ICSetElem_DenseAddImpl(JitCode *stubCode, ObjectGroup *group, const AutoShapeVector *shapes) : ICSetElem_DenseAdd(stubCode, group, ProtoChainDepth) { MOZ_ASSERT(shapes->length() == NumShapes); for (size_t i = 0; i < NumShapes; i++) shapes_[i].init((*shapes)[i]); } public: static inline ICSetElem_DenseAddImpl *New(ICStubSpace *space, JitCode *code, - types::ObjectGroup *group, + ObjectGroup *group, const AutoShapeVector *shapes) { if (!code) return nullptr; return space->allocate<ICSetElem_DenseAddImpl<ProtoChainDepth> >(code, group, shapes); } void traceShapes(JSTracer *trc) { @@ -4580,17 +4580,17 @@ class ICGetProp_Unboxed : public ICMonit bool generateStubCode(MacroAssembler &masm); virtual int32_t getKey() const { return static_cast<int32_t>(kind) | (static_cast<int32_t>(fieldType_)) << 16; } public: Compiler(JSContext *cx, ICStub *firstMonitorStub, - types::ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) + ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) : ICStubCompiler(cx, ICStub::GetProp_Unboxed), firstMonitorStub_(firstMonitorStub), group_(cx, group), fieldOffset_(fieldOffset), fieldType_(fieldType) {} ICStub *getStub(ICStubSpace *space) { @@ -5540,17 +5540,17 @@ class ICSetProp_Unboxed : public ICUpdat bool generateStubCode(MacroAssembler &masm); virtual int32_t getKey() const { return static_cast<int32_t>(kind) | (static_cast<int32_t>(fieldType_) << 16); } public: - Compiler(JSContext *cx, types::ObjectGroup *group, uint32_t fieldOffset, + Compiler(JSContext *cx, ObjectGroup *group, uint32_t fieldOffset, JSValueType fieldType) : ICStubCompiler(cx, ICStub::SetProp_Unboxed), group_(cx, group), fieldOffset_(fieldOffset), fieldType_(fieldType) {} ICUpdatedStub *getStub(ICStubSpace *space) { @@ -5630,17 +5630,17 @@ class ICSetProp_TypedObject : public ICU virtual int32_t getKey() const { return static_cast<int32_t>(kind) | (static_cast<int32_t>(SimpleTypeDescrKey(fieldDescr_)) << 16) | (static_cast<int32_t>(layout_) << 24); } public: - Compiler(JSContext *cx, Shape *shape, types::ObjectGroup *group, uint32_t fieldOffset, + Compiler(JSContext *cx, Shape *shape, ObjectGroup *group, uint32_t fieldOffset, SimpleTypeDescr *fieldDescr) : ICStubCompiler(cx, ICStub::SetProp_TypedObject), shape_(cx, shape), group_(cx, group), fieldOffset_(fieldOffset), layout_(GetTypedThingLayout(shape->getObjectClass())), fieldDescr_(cx, fieldDescr) {}
--- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -94,17 +94,17 @@ BaselineInspector::maybeInfoForPropertyO return true; MOZ_ASSERT(isValidPC(pc)); const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.firstStub(); while (stub->next()) { Shape *shape = nullptr; - types::ObjectGroup *group = nullptr; + ObjectGroup *group = nullptr; if (stub->isGetProp_Native()) { shape = stub->toGetProp_Native()->shape(); } else if (stub->isSetProp_Native()) { shape = stub->toSetProp_Native()->shape(); } else if (stub->isGetProp_Unboxed()) { group = stub->toGetProp_Unboxed()->group(); } else if (stub->isSetProp_Unboxed()) { group = stub->toSetProp_Unboxed()->group();
--- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -88,17 +88,17 @@ class BaselineInspector return ICInspectorType(this, pc, ent); } ICStub *monomorphicStub(jsbytecode *pc); bool dimorphicStub(jsbytecode *pc, ICStub **pfirst, ICStub **psecond); public: typedef Vector<Shape *, 4, JitAllocPolicy> ShapeVector; - typedef Vector<types::ObjectGroup *, 4, JitAllocPolicy> ObjectGroupVector; + typedef Vector<ObjectGroup *, 4, JitAllocPolicy> ObjectGroupVector; bool maybeInfoForPropertyOp(jsbytecode *pc, ShapeVector &nativeShapes, ObjectGroupVector &unboxedGroups); SetElemICInspector setElemICInspector(jsbytecode *pc) { return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback); }
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -718,17 +718,17 @@ CodeGenerator::visitFunctionDispatch(LFu casesWithFallback = mir->numCases() + 1; lastLabel = skipTrivialBlocks(mir->getFallback())->lir()->label(); } // Compare function pointers, except for the last case. for (size_t i = 0; i < casesWithFallback - 1; i++) { MOZ_ASSERT(i < mir->numCases()); LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir(); - if (types::ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) { + if (ObjectGroup *funcGroup = mir->getCaseObjectGroup(i)) { masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfGroup()), ImmGCPtr(funcGroup), target->label()); } else { JSFunction *func = mir->getCase(i); masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label()); } } @@ -759,17 +759,17 @@ CodeGenerator::visitObjectGroupDispatch( DebugOnly<bool> found = false; for (size_t j = 0; j < propTable->numEntries(); j++) { if (propTable->getFunction(j) != func) continue; if (lastBranch.isInitialized()) lastBranch.emit(masm); - types::ObjectGroup *group = propTable->getObjectGroup(j); + ObjectGroup *group = propTable->getObjectGroup(j); lastBranch = MacroAssembler::BranchGCPtr(Assembler::Equal, temp, ImmGCPtr(group), target->label()); lastBlock = target; found = true; } MOZ_ASSERT(found); } @@ -3675,18 +3675,18 @@ CodeGenerator::emitObjectOrStringResultC masm.jump(&ok); masm.bind(&miss); // Type set guards might miss when an object's group changes and its // properties become unknown, so check for this case. masm.loadPtr(Address(output, JSObject::offsetOfGroup()), temp); masm.branchTestPtr(Assembler::NonZero, - Address(temp, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); + Address(temp, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.assumeUnreachable("MIR instruction returned object with unexpected type"); masm.bind(&ok); } // Check that we have a valid GC pointer. saveVolatile(); @@ -3755,18 +3755,18 @@ CodeGenerator::emitValueResultChecks(LIn // Type set guards might miss when an object's group changes and its // properties become unknown, so check for this case. Label realMiss; masm.branchTestObject(Assembler::NotEqual, output, &realMiss); Register payload = masm.extractObject(output, temp1); masm.loadPtr(Address(payload, JSObject::offsetOfGroup()), temp1); masm.branchTestPtr(Assembler::NonZero, - Address(temp1, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); + Address(temp1, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &ok); masm.bind(&realMiss); masm.assumeUnreachable("MIR instruction returned value with unexpected type"); masm.bind(&ok); } // Check that we have a valid GC pointer. @@ -3942,17 +3942,17 @@ void CodeGenerator::visitNewArrayCallVM(LNewArray *lir) { Register objReg = ToRegister(lir->output()); MOZ_ASSERT(!lir->isCall()); saveLive(lir); JSObject *templateObject = lir->mir()->templateObject(); - types::ObjectGroup *group = + ObjectGroup *group = templateObject->isSingleton() ? nullptr : templateObject->group(); pushArg(Imm32(lir->mir()->allocatingBehaviour())); pushArg(ImmGCPtr(group)); pushArg(Imm32(lir->mir()->count())); callVM(NewDenseArrayInfo, lir); @@ -4333,25 +4333,25 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox Register temp = ToRegister(lir->temp()); Label bail; // obj->group() masm.loadPtr(Address(object, JSObject::offsetOfGroup()), temp); // Guard that the object has the same representation as the one produced for // SIMD value-type. - Address clasp(temp, types::ObjectGroup::offsetOfClasp()); + Address clasp(temp, ObjectGroup::offsetOfClasp()); static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent"); masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_), &bail); // obj->type()->typeDescr() // The previous class pointer comparison implies that the addendumKind is // Addendum_TypeDescr. - masm.loadPtr(Address(temp, types::ObjectGroup::offsetOfAddendum()), temp); + masm.loadPtr(Address(temp, ObjectGroup::offsetOfAddendum()), temp); // Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32 // Value which is equivalent to the object class check. static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots"); Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND)); masm.assertTestInt32(Assembler::Equal, typeDescrKind, "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())"); masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail); @@ -4805,17 +4805,17 @@ CodeGenerator::visitTypedArrayElements(L void CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr *lir) { Register obj = ToRegister(lir->object()); Register out = ToRegister(lir->output()); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), out); - masm.loadPtr(Address(out, types::ObjectGroup::offsetOfAddendum()), out); + masm.loadPtr(Address(out, ObjectGroup::offsetOfAddendum()), out); } void CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir) { Register obj = ToRegister(lir->object()); Register out = ToRegister(lir->output());
--- a/js/src/jit/Disassembler.h +++ b/js/src/jit/Disassembler.h @@ -2,16 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_Disassembler_h #define jit_Disassembler_h +#include "jit/MacroAssembler.h" #include "jit/Registers.h" namespace js { namespace jit { namespace Disassembler { class ComplexAddress {
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2875,35 +2875,32 @@ jit::ConvertLinearInequality(TempAllocat MCompare *compare = MCompare::New(alloc, lhsDef, rhsDef, op); block->insertAtEnd(compare); compare->setCompareType(MCompare::Compare_Int32); return compare; } static bool -AnalyzePoppedThis(JSContext *cx, types::ObjectGroup *group, +AnalyzePoppedThis(JSContext *cx, ObjectGroup *group, MDefinition *thisValue, MInstruction *ins, bool definitelyExecuted, HandlePlainObject baseobj, Vector<types::TypeNewScript::Initializer> *initializerList, Vector<PropertyName *> *accessedProperties, bool *phandled) { // Determine the effect that a use of the |this| value when calling |new| // on a script has on the properties definitely held by the new object. if (ins->isCallSetProperty()) { MCallSetProperty *setprop = ins->toCallSetProperty(); if (setprop->object() != thisValue) return true; - // Don't use GetAtomId here, we need to watch for SETPROP on - // integer properties and bail out. We can't mark the aggregate - // JSID_VOID type property as being in a definite slot. if (setprop->name() == cx->names().prototype || setprop->name() == cx->names().proto || setprop->name() == cx->names().constructor) { return true; } // Ignore assignments to properties that were already written to. @@ -3011,17 +3008,17 @@ static int CmpInstructions(const void *a, const void *b) { return (*static_cast<MInstruction * const *>(a))->id() - (*static_cast<MInstruction * const *>(b))->id(); } bool jit::AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::ObjectGroup *group, HandlePlainObject baseobj, + ObjectGroup *group, HandlePlainObject baseobj, Vector<types::TypeNewScript::Initializer> *initializerList) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); // When invoking 'new' on the specified script, try to find some properties // which will definitely be added to the created object before it has a // chance to escape and be accessed elsewhere.
--- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -165,17 +165,17 @@ ConvertLinearSum(TempAllocator &alloc, M // Convert the test 'sum >= 0' to a comparison, adding any necessary // instructions to the end of block. MCompare * ConvertLinearInequality(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum); bool AnalyzeNewScriptDefiniteProperties(JSContext *cx, JSFunction *fun, - types::ObjectGroup *group, HandlePlainObject baseobj, + ObjectGroup *group, HandlePlainObject baseobj, Vector<types::TypeNewScript::Initializer> *initializerList); bool AnalyzeArgumentsUsage(JSContext *cx, JSScript *script); bool DeadIfUnused(const MDefinition *def);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -354,17 +354,17 @@ IonBuilder::getPolyCallTargets(types::Te if (!targets.reserve(objCount)) return false; for (unsigned i = 0; i < objCount; i++) { JSObject *obj = calleeTypes->getSingleton(i); if (obj) { MOZ_ASSERT(obj->isSingleton()); } else { - types::ObjectGroup *group = calleeTypes->getGroup(i); + ObjectGroup *group = calleeTypes->getGroup(i); if (!group) continue; obj = group->maybeInterpretedFunction(); if (!obj) { targets.clear(); return true; } @@ -5317,17 +5317,17 @@ IonBuilder::inlineCalls(CallInfo &callIn // inlineSingleCall() changed |current| to the inline return block. MBasicBlock *inlineReturnBlock = current; setCurrent(dispatchBlock); // Connect the inline path to the returnBlock. // // Note that guarding is on the original function pointer even // if there is a clone, since cloning occurs at the callsite. - types::ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group(); + ObjectGroup *funcGroup = original->isSingleton() ? nullptr : original->group(); dispatch->addCase(original, funcGroup, inlineBlock); MDefinition *retVal = inlineReturnBlock->peek(-1); retPhi->addInput(retVal); inlineReturnBlock->end(MGoto::New(alloc(), returnBlock)); if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock)) return false; } @@ -5538,17 +5538,17 @@ IonBuilder::createThisScriptedSingleton( if (!templateObject) return nullptr; if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>()) return nullptr; if (!templateObject->hasTenuredProto() || templateObject->getProto() != proto) return nullptr; types::TypeSetObjectKey *templateObjectKey = types::TypeSetObjectKey::get(templateObject->group()); - if (templateObjectKey->hasFlags(constraints(), types::OBJECT_FLAG_NEW_SCRIPT_CLEARED)) + if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED)) return nullptr; types::StackTypeSet *thisTypes = types::TypeScript::ThisTypes(target->nonLazyScript()); if (!thisTypes || !thisTypes->hasType(types::Type::ObjectType(templateObject))) return nullptr; // Generate an inline path to create a new |this| object with // the given singleton prototype. @@ -6245,24 +6245,24 @@ IonBuilder::jsop_newarray(uint32_t count else templateObject->as<ArrayObject>().clearShouldConvertDoubleElements(); return true; } bool IonBuilder::jsop_newarray_copyonwrite() { - ArrayObject *templateObject = types::GetCopyOnWriteObject(script(), pc); + ArrayObject *templateObject = ObjectGroup::getCopyOnWriteObject(script(), pc); // The baseline compiler should have ensured the template object has a type // with the copy on write flag set already. During the arguments usage // analysis the baseline compiler hasn't run yet, however, though in this // case the template object's type doesn't matter. MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage, - templateObject->group()->hasAnyFlags(types::OBJECT_FLAG_COPY_ON_WRITE)); + templateObject->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); MNewArrayCopyOnWrite *ins = MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject, templateObject->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -6321,17 +6321,17 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; if (obj->isUnknownValue()) { needStub = true; } else { types::TypeSetObjectKey *initializer = obj->resultTypeSet()->getObject(0); if (value->type() == MIRType_MagicHole) { - if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) + if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; } else if (!initializer->unknownProperties()) { types::HeapTypeSetKey elemTypes = initializer->property(JSID_VOID); if (!TypeSetIncludes(elemTypes.maybeTypes(), value->type(), value->resultTypeSet())) { elemTypes.freeze(constraints()); needStub = true; } } @@ -7624,17 +7624,17 @@ IonBuilder::checkTypedObjectIndexInBound int32_t lenOfAll; MDefinition *length; if (objPrediction.hasKnownArrayLength(&lenOfAll)) { length = constantInt(lenOfAll); // If we are not loading the length from the object itself, only // optimize if the array buffer can't have been neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) { + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) { trackOptimizationOutcome(TrackedOutcome::TypedObjectNeutered); return false; } } else { trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange); return false; } @@ -9077,34 +9077,34 @@ IonBuilder::jsop_length_fastPath() } if (obj->mightBeType(MIRType_Object)) { types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); // Compute the length for array objects. if (objTypes && objTypes->getKnownClass(constraints()) == &ArrayObject::class_ && - !objTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_LENGTH_OVERFLOW)) + !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) { current->pop(); MElements *elements = MElements::New(alloc(), obj); current->add(elements); // Read length. MArrayLength *length = MArrayLength::New(alloc(), elements); current->add(length); current->push(length); return true; } // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return false; MInstruction *length; int32_t sizedLength; if (prediction.hasKnownArrayLength(&sizedLength)) { obj->setImplicitlyUsedUnchecked(); length = MConstant::New(alloc(), Int32Value(sizedLength)); } else { @@ -9225,17 +9225,17 @@ IonBuilder::getDefiniteSlot(types::Tempo // objects. Later, this will let us elide baseline IC stubs for preliminary // objects, which often have a different number of fixed slots from // subsequent objects. for (size_t i = 0; i < types->getObjectCount(); i++) { types::TypeSetObjectKey *key = types->getObject(i); if (!key) continue; - if (types::ObjectGroup *group = key->maybeGroup()) { + if (ObjectGroup *group = key->maybeGroup()) { if (group->newScript() && !group->newScript()->analyzed()) { addAbortedNewScriptPropertiesGroup(group); trackOptimizationOutcome(TrackedOutcome::NoAnalysisInfo); return UINT32_MAX; } } } @@ -9533,17 +9533,17 @@ IonBuilder::annotateGetPropertyCache(MDe InlinePropertyTable *inlinePropTable = getPropCache->initInlinePropertyTable(alloc(), pc); if (!inlinePropTable) return false; // Ensure that the relevant property typeset for each group is // is a single-object typeset containing a JSFunction for (unsigned int i = 0; i < objCount; i++) { - types::ObjectGroup *group = objTypes->getGroup(i); + ObjectGroup *group = objTypes->getGroup(i); if (!group) continue; types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(group); if (key->unknownProperties() || !key->hasTenuredProto() || !key->proto().isObject()) continue; const Class *clasp = key->clasp(); if (!ClassHasEffectlessLookup(clasp, name) || ClassHasResolveHook(compartment, clasp, name)) @@ -9983,17 +9983,17 @@ IonBuilder::getPropTryScalarPropOfTypedO int32_t fieldOffset, TypedObjectPrediction fieldPrediction) { // Must always be loading the same scalar type Scalar::Type fieldType = fieldPrediction.scalarType(); // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; trackOptimizationSuccess(); *emitted = true; LinearSum byteOffset(alloc()); if (!byteOffset.add(fieldOffset)) setForceAbort(); @@ -10005,17 +10005,17 @@ bool IonBuilder::getPropTryReferencePropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldPrediction, PropertyName *name) { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; trackOptimizationSuccess(); *emitted = true; LinearSum byteOffset(alloc()); if (!byteOffset.add(fieldOffset)) setForceAbort(); @@ -10027,17 +10027,17 @@ bool IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, MDefinition *typedObj, int32_t fieldOffset, TypedObjectPrediction fieldPrediction, size_t fieldIndex) { // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; // OK, perform the optimization // Identify the type object for the field. MDefinition *type = loadTypedObjectType(typedObj); MDefinition *fieldTypeObj = typeObjectForFieldFromStructType(type, fieldIndex); @@ -10425,17 +10425,17 @@ IonBuilder::getPropTryInlineAccess(bool trackOptimizationOutcome(TrackedOutcome::Monomorphic); *emitted = true; return true; } if (nativeShapes.empty() && unboxedGroups.length() == 1) { spew("Inlining monomorphic unboxed GETPROP"); - types::ObjectGroup *group = unboxedGroups[0]; + ObjectGroup *group = unboxedGroups[0]; // Failures in this group guard should be treated the same as a shape guard failure. obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, Bailout_ShapeGuard); current->add(obj->toInstruction()); if (failedShapeGuard_) obj->toGuardObjectGroup()->setNotMovable(); @@ -10599,17 +10599,17 @@ IonBuilder::tryInnerizeWindow(MDefinitio JSObject *inner = GetInnerObject(singleton); if (inner == singleton || inner != &script()->global()) return obj; // When we navigate, the outer object is brain transplanted and we'll mark // its ObjectGroup as having unknown properties. The type constraint we add // here will invalidate JIT code when this happens. types::TypeSetObjectKey *key = types::TypeSetObjectKey::get(singleton); - if (key->hasFlags(constraints(), types::OBJECT_FLAG_UNKNOWN_PROPERTIES)) + if (key->hasFlags(constraints(), OBJECT_FLAG_UNKNOWN_PROPERTIES)) return obj; obj->setImplicitlyUsedUnchecked(); return constant(ObjectValue(script()->global())); } bool IonBuilder::getPropTryInnerize(bool *emitted, MDefinition *obj, PropertyName *name, @@ -10878,17 +10878,17 @@ IonBuilder::setPropTryReferencePropOfTyp int32_t fieldOffset, MDefinition *value, TypedObjectPrediction fieldPrediction, PropertyName *name) { ReferenceTypeDescr::Type fieldType = fieldPrediction.referenceType(); types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); if (!byteOffset.add(fieldOffset)) setForceAbort(); if (!storeReferenceTypedObjectValue(obj, byteOffset, fieldType, value, name)) return true; @@ -10907,17 +10907,17 @@ IonBuilder::setPropTryScalarPropOfTypedO MDefinition *value, TypedObjectPrediction fieldPrediction) { // Must always be loading the same scalar type Scalar::Type fieldType = fieldPrediction.scalarType(); // Don't optimize if the typed object might be neutered. types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (globalKey->hasFlags(constraints(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) + if (globalKey->hasFlags(constraints(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED)) return true; LinearSum byteOffset(alloc()); if (!byteOffset.add(fieldOffset)) setForceAbort(); if (!storeScalarTypedObjectValue(obj, byteOffset, fieldType, false, value)) return false; @@ -11104,17 +11104,17 @@ IonBuilder::setPropTryInlineAccess(bool trackOptimizationOutcome(TrackedOutcome::Monomorphic); *emitted = true; return true; } if (nativeShapes.empty() && unboxedGroups.length() == 1) { spew("Inlining monomorphic unboxed SETPROP"); - types::ObjectGroup *group = unboxedGroups[0]; + ObjectGroup *group = unboxedGroups[0]; // Failures in this group guard should be treated the same as a shape guard failure. obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false, Bailout_ShapeGuard); current->add(obj->toInstruction()); if (failedShapeGuard_) obj->toGuardObjectGroup()->setNotMovable(); @@ -11239,17 +11239,17 @@ IonBuilder::jsop_regexp(RegExpObject *re // by getting or setting properties on it. // // First, make sure the regex is one we can safely optimize. Lowering can // then check if this regex object only flows into known natives and can // avoid cloning in this case. bool mustClone = true; types::TypeSetObjectKey *globalKey = types::TypeSetObjectKey::get(&script()->global()); - if (!globalKey->hasFlags(constraints(), types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { + if (!globalKey->hasFlags(constraints(), OBJECT_FLAG_REGEXP_FLAGS_SET)) { #ifdef DEBUG // Only compare the statics if the one on script()->global() has been // instantiated. if (script()->global().hasRegExpStatics()) { RegExpStatics *res = script()->global().getAlreadyCreatedRegExpStatics(); MOZ_ASSERT(res); uint32_t origFlags = reobj->getFlags(); uint32_t staticsFlags = res->getFlags(); @@ -11628,17 +11628,17 @@ bool IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) { JSScript *outerScript = ScopeCoordinateFunctionScript(script(), pc); if (!outerScript || !outerScript->treatAsRunOnce()) return false; types::TypeSetObjectKey *funKey = types::TypeSetObjectKey::get(outerScript->functionNonDelazifying()); - if (funKey->hasFlags(constraints(), types::OBJECT_FLAG_RUNONCE_INVALIDATED)) + if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, // so there will be only one call object and the aliased var access can be // compiled in the same manner as a global access. We still need to find // the call object though. // Look for the call object on the current script's function's scope chain. @@ -12098,17 +12098,17 @@ IonBuilder::typedObjectPrediction(types: return TypedObjectPrediction(); // And only known objects. if (types->unknownObject()) return TypedObjectPrediction(); TypedObjectPrediction out; for (uint32_t i = 0; i < types->getObjectCount(); i++) { - types::ObjectGroup *group = types->getGroup(i); + ObjectGroup *group = types->getGroup(i); if (!group || !types::TypeSetObjectKey::get(group)->hasStableClassAndProto(constraints())) return TypedObjectPrediction(); if (!IsTypedObjectClass(group->clasp())) return TypedObjectPrediction(); out.addDescr(group->typeDescr()); }
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -483,17 +483,17 @@ GeneratePrototypeGuards(JSContext *cx, I * in reshaping the holder, and thus the failure of the shape guard. */ MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // Note: objectReg and scratchReg may be the same register, so we cannot // use objectReg in the rest of this function. masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg); - Address proto(scratchReg, types::ObjectGroup::offsetOfProto()); + Address proto(scratchReg, ObjectGroup::offsetOfProto()); masm.branchPtr(Assembler::NotEqual, proto, ImmMaybeNurseryPtr(obj->getProto()), failures); } JSObject *pobj = IsCacheableDOMProxy(obj) ? obj->getTaggedProto().toObjectOrNull() : obj->getProto(); if (!pobj) @@ -1892,17 +1892,17 @@ IonCache::destroy() // Jump to failure if a value being written is not a property for obj/id. // This might clobber |object|. static void CheckTypeSetForWrite(MacroAssembler &masm, JSObject *obj, jsid id, Register object, ConstantOrRegister value, Label *failure) { TypedOrValueRegister valReg = value.reg(); - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); if (group->unknownProperties()) return; types::HeapTypeSet *propTypes = group->maybeGetProperty(id); MOZ_ASSERT(propTypes); // guardTypeSet can read from type sets without triggering read barriers. types::TypeSet::readBarrier(propTypes); @@ -1924,17 +1924,17 @@ GenerateSetSlot(JSContext *cx, MacroAsse // Guard that the incoming value is in the type set for the property // if a type barrier is required. if (needsTypeBarrier) { // We can't do anything that would change the HeapTypeSet, so // just guard that it's already there. // Obtain and guard on the ObjectGroup of the object. - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(group), &failures); if (checkTypeset) { masm.push(object); CheckTypeSetForWrite(masm, obj, shape->propid(), object, value, &barrierFailure); masm.pop(object); @@ -2458,17 +2458,17 @@ SetPropertyIC::attachCallSetter(JSContex masm.bind(&failure); attacher.jumpNextStub(masm); return linkAndAttachStub(cx, masm, attacher, ion, "setter call"); } static void GenerateAddSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher, - NativeObject *obj, Shape *oldShape, types::ObjectGroup *oldGroup, + NativeObject *obj, Shape *oldShape, ObjectGroup *oldGroup, Register object, ConstantOrRegister value, bool checkTypeset) { MOZ_ASSERT(obj->isNative()); Label failures; // Guard the type of the object @@ -2515,17 +2515,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse // Changing object's group from a partially to fully initialized group, // per the acquired properties analysis. Only change the group if the // old group still has a newScript. Label noTypeChange, skipPop; masm.push(object); masm.loadPtr(Address(object, JSObject::offsetOfGroup()), object); masm.branchPtr(Assembler::Equal, - Address(object, types::ObjectGroup::offsetOfAddendum()), + Address(object, ObjectGroup::offsetOfAddendum()), ImmWord(0), &noTypeChange); masm.pop(object); Address groupAddr(object, JSObject::offsetOfGroup()); if (cx->zone()->needsIncrementalBarrier()) masm.callPreBarrier(groupAddr, MIRType_ObjectGroup); masm.storePtr(ImmGCPtr(obj->group()), groupAddr); @@ -2574,17 +2574,17 @@ SetPropertyIC::attachAddSlot(JSContext * GenerateAddSlot(cx, masm, attacher, obj, oldShape, oldGroup, object(), value(), checkTypeset); return linkAndAttachStub(cx, masm, attacher, ion, "adding"); } static bool CanInlineSetPropTypeCheck(JSObject *obj, jsid id, ConstantOrRegister val, bool *checkTypeset) { bool shouldCheck = false; - types::ObjectGroup *group = obj->group(); + ObjectGroup *group = obj->group(); if (!group->unknownProperties()) { types::HeapTypeSet *propTypes = group->maybeGetProperty(id); if (!propTypes) return false; if (!propTypes->unknown()) { if (obj->isSingleton() && !propTypes->nonConstantProperty()) return false; shouldCheck = true;
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -501,20 +501,20 @@ IonBuilder::inlineArrayPopShift(CallInfo if (returnType == MIRType_Undefined || returnType == MIRType_Null) return InliningStatus_NotInlined; if (callInfo.thisArg()->type() != MIRType_Object) return InliningStatus_NotInlined; // Pop and shift are only handled for dense arrays that have never been // used in an iterator: popping elements does not account for suppressing // deleted properties in active iterators. - types::ObjectGroupFlags unhandledFlags = - types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW | - types::OBJECT_FLAG_ITERATED; + ObjectGroupFlags unhandledFlags = + OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW | + OBJECT_FLAG_ITERATED; MDefinition *obj = callInfo.thisArg(); types::TemporaryTypeSet *thisTypes = obj->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; @@ -525,17 +525,17 @@ IonBuilder::inlineArrayPopShift(CallInfo return InliningStatus_NotInlined; } callInfo.setImplicitlyUsedUnchecked(); obj = addMaybeCopyElementsForWrite(obj); types::TemporaryTypeSet *returnTypes = getInlineReturnTypeSet(); - bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED); + bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED); bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType()); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, nullptr, returnTypes); if (barrier != BarrierKind::NoBarrier) returnType = MIRType_Value; MArrayPopShift *ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined); @@ -638,18 +638,18 @@ IonBuilder::inlineArrayPush(CallInfo &ca if (getInlineReturnType() != MIRType_Int32) return InliningStatus_NotInlined; if (callInfo.thisArg()->type() != MIRType_Object) return InliningStatus_NotInlined; types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; @@ -706,54 +706,54 @@ IonBuilder::inlineArrayConcat(CallInfo & // |this| and the argument must be dense arrays. types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet(); types::TemporaryTypeSet *argTypes = callInfo.getArg(0)->resultTypeSet(); if (!thisTypes || !argTypes) return InliningStatus_NotInlined; if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_) return InliningStatus_NotInlined; - if (argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_SPARSE_INDEXES | - types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | + OBJECT_FLAG_LENGTH_OVERFLOW)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } // Watch out for indexed properties on the prototype. if (types::ArrayPrototypeHasIndexedProperty(constraints(), script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); return InliningStatus_NotInlined; } // Require the 'this' types to have a specific type matching the current // global, so we can create the result object inline. if (thisTypes->getObjectCount() != 1) return InliningStatus_NotInlined; - types::ObjectGroup *thisGroup = thisTypes->getGroup(0); + ObjectGroup *thisGroup = thisTypes->getGroup(0); if (!thisGroup) return InliningStatus_NotInlined; types::TypeSetObjectKey *thisKey = types::TypeSetObjectKey::get(thisGroup); if (thisKey->unknownProperties()) return InliningStatus_NotInlined; // Don't inline if 'this' is packed and the argument may not be packed // (the result array will reuse the 'this' type). - if (!thisTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED) && - argTypes->hasObjectFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) + if (!thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED) && + argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); return InliningStatus_NotInlined; } // Constraints modeling this concat have not been generated by inference, // so check that type information already reflects possible side effects of // this call.
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4316,33 +4316,33 @@ jit::ElementAccessIsAnyTypedArray(types: *arrayType = types->getSharedTypedArrayType(constraints); return *arrayType != Scalar::MaxTypedArrayViewType; } bool jit::ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - return types && !types->hasObjectFlags(constraints, types::OBJECT_FLAG_NON_PACKED); + return types && !types->hasObjectFlags(constraints, OBJECT_FLAG_NON_PACKED); } bool jit::ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - return !types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_COPY_ON_WRITE); + return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE); } bool jit::ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, MDefinition *obj) { types::TemporaryTypeSet *types = obj->resultTypeSet(); - if (!types || types->hasObjectFlags(constraints, types::OBJECT_FLAG_LENGTH_OVERFLOW)) + if (!types || types->hasObjectFlags(constraints, OBJECT_FLAG_LENGTH_OVERFLOW)) return true; return types::TypeCanHaveExtraIndexedProperties(constraints, types); } MIRType jit::DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj) {
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -6314,17 +6314,17 @@ class MStringSplit return getOperand(0); } MDefinition *separator() const { return getOperand(1); } JSObject *templateObject() const { return &getOperand(2)->toConstant()->value().toObject(); } - types::ObjectGroup *group() const { + ObjectGroup *group() const { return templateObject()->group(); } bool possiblyCalls() const MOZ_OVERRIDE { return true; } virtual AliasSet getAliasSet() const MOZ_OVERRIDE { // Although this instruction returns a new array, we don't have to mark // it as store instruction, see also MNewArray. @@ -7161,17 +7161,17 @@ struct LambdaFunctionInfo bool useSingletonForClone; explicit LambdaFunctionInfo(JSFunction *fun) : fun(fun), flags(fun->flags()), scriptOrLazyScript(fun->hasScript() ? (gc::Cell *) fun->nonLazyScript() : (gc::Cell *) fun->lazyScript()), singletonType(fun->isSingleton()), - useSingletonForClone(types::UseSingletonForClone(fun)) + useSingletonForClone(ObjectGroup::useSingletonForClone(fun)) {} LambdaFunctionInfo(const LambdaFunctionInfo &info) : fun((JSFunction *) info.fun), flags(info.flags), scriptOrLazyScript(info.scriptOrLazyScript), singletonType(info.singletonType), useSingletonForClone(info.useSingletonForClone) {} @@ -7182,17 +7182,17 @@ class MLambda public SingleObjectPolicy::Data { LambdaFunctionInfo info_; MLambda(types::CompilerConstraintList *constraints, MDefinition *scopeChain, MConstant *cst) : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as<JSFunction>()) { setResultType(MIRType_Object); - if (!info().fun->isSingleton() && !types::UseSingletonForClone(info().fun)) + if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun)) setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun)); } public: INSTRUCTION_HEADER(Lambda) static MLambda *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, MDefinition *scopeChain, MConstant *fun) @@ -7220,17 +7220,17 @@ class MLambdaArrow { LambdaFunctionInfo info_; MLambdaArrow(types::CompilerConstraintList *constraints, MDefinition *scopeChain, MDefinition *this_, JSFunction *fun) : MBinaryInstruction(scopeChain, this_), info_(fun) { setResultType(MIRType_Object); - MOZ_ASSERT(!types::UseSingletonForClone(fun)); + MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun)); if (!fun->isSingleton()) setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); } public: INSTRUCTION_HEADER(LambdaArrow) static MLambdaArrow *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, @@ -9196,20 +9196,20 @@ class MStoreFixedSlot }; typedef Vector<JSObject *, 4, JitAllocPolicy> ObjectVector; typedef Vector<bool, 4, JitAllocPolicy> BoolVector; class InlinePropertyTable : public TempObject { struct Entry : public TempObject { - AlwaysTenured<types::ObjectGroup *> group; + AlwaysTenured<ObjectGroup *> group; AlwaysTenuredFunction func; - Entry(types::ObjectGroup *group, JSFunction *func) + Entry(ObjectGroup *group, JSFunction *func) : group(group), func(func) { } }; jsbytecode *pc_; MResumePoint *priorResumePoint_; Vector<Entry *, 4, JitAllocPolicy> entries_; @@ -9227,25 +9227,25 @@ class InlinePropertyTable : public TempO priorResumePoint_ = nullptr; return rp; } jsbytecode *pc() const { return pc_; } - bool addEntry(TempAllocator &alloc, types::ObjectGroup *group, JSFunction *func) { + bool addEntry(TempAllocator &alloc, ObjectGroup *group, JSFunction *func) { return entries_.append(new(alloc) Entry(group, func)); } size_t numEntries() const { return entries_.length(); } - types::ObjectGroup *getObjectGroup(size_t i) const { + ObjectGroup *getObjectGroup(size_t i) const { MOZ_ASSERT(i < numEntries()); return entries_[i]->group; } JSFunction *getFunction(size_t i) const { MOZ_ASSERT(i < numEntries()); return entries_[i]->func; } @@ -9375,17 +9375,17 @@ class MGetPropertyPolymorphic // The shape to guard against. Shape *objShape; // The property to laod. Shape *shape; }; Vector<Entry, 4, JitAllocPolicy> nativeShapes_; - Vector<types::ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_; + Vector<ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_; AlwaysTenuredPropertyName name_; MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name) : MUnaryInstruction(obj), nativeShapes_(alloc), unboxedGroups_(alloc), name_(name) { @@ -9410,32 +9410,32 @@ class MGetPropertyPolymorphic } bool addShape(Shape *objShape, Shape *shape) { Entry entry; entry.objShape = objShape; entry.shape = shape; return nativeShapes_.append(entry); } - bool addUnboxedGroup(types::ObjectGroup *group) { + bool addUnboxedGroup(ObjectGroup *group) { return unboxedGroups_.append(group); } size_t numShapes() const { return nativeShapes_.length(); } Shape *objShape(size_t i) const { return nativeShapes_[i].objShape; } Shape *shape(size_t i) const { return nativeShapes_[i].shape; } size_t numUnboxedGroups() const { return unboxedGroups_.length(); } - types::ObjectGroup *unboxedGroup(size_t i) const { + ObjectGroup *unboxedGroup(size_t i) const { return unboxedGroups_[i]; } PropertyName *name() const { return name_; } MDefinition *obj() const { return getOperand(0); } @@ -9457,17 +9457,17 @@ class MSetPropertyPolymorphic // The shape to guard against. Shape *objShape; // The property to load. Shape *shape; }; Vector<Entry, 4, JitAllocPolicy> nativeShapes_; - Vector<types::ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_; + Vector<ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_; AlwaysTenuredPropertyName name_; bool needsBarrier_; MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value, PropertyName *name) : MBinaryInstruction(obj, value), nativeShapes_(alloc), unboxedGroups_(alloc), @@ -9485,32 +9485,32 @@ class MSetPropertyPolymorphic } bool addShape(Shape *objShape, Shape *shape) { Entry entry; entry.objShape = objShape; entry.shape = shape; return nativeShapes_.append(entry); } - bool addUnboxedGroup(types::ObjectGroup *group) { + bool addUnboxedGroup(ObjectGroup *group) { return unboxedGroups_.append(group); } size_t numShapes() const { return nativeShapes_.length(); } Shape *objShape(size_t i) const { return nativeShapes_[i].objShape; } Shape *shape(size_t i) const { return nativeShapes_[i].shape; } size_t numUnboxedGroups() const { return unboxedGroups_.length(); } - types::ObjectGroup *unboxedGroup(size_t i) const { + ObjectGroup *unboxedGroup(size_t i) const { return unboxedGroups_[i]; } PropertyName *name() const { return name_; } MDefinition *obj() const { return getOperand(0); } @@ -9534,20 +9534,20 @@ class MDispatchInstruction public SingleObjectPolicy::Data { // Map from JSFunction* -> MBasicBlock. struct Entry { JSFunction *func; // If |func| has a singleton group, |funcGroup| is null. Otherwise, // |funcGroup| holds the ObjectGroup for |func|, and dispatch guards // on the group instead of directly on the function. - types::ObjectGroup *funcGroup; + ObjectGroup *funcGroup; MBasicBlock *block; - Entry(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) + Entry(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block) : func(func), funcGroup(funcGroup), block(block) { } }; Vector<Entry, 4, JitAllocPolicy> map_; // An optional fallback path that uses MCall. MBasicBlock *fallback_; MUse operand_; @@ -9606,26 +9606,26 @@ class MDispatchInstruction MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE { MOZ_ASSERT(i < numSuccessors()); if (i == map_.length()) return fallback_; return map_[i].block; } public: - void addCase(JSFunction *func, types::ObjectGroup *funcGroup, MBasicBlock *block) { + void addCase(JSFunction *func, ObjectGroup *funcGroup, MBasicBlock *block) { map_.append(Entry(func, funcGroup, block)); } uint32_t numCases() const { return map_.length(); } JSFunction *getCase(uint32_t i) const { return map_[i].func; } - types::ObjectGroup *getCaseObjectGroup(uint32_t i) const { + ObjectGroup *getCaseObjectGroup(uint32_t i) const { return map_[i].funcGroup; } MBasicBlock *getCaseBlock(uint32_t i) const { return map_[i].block; } bool hasFallback() const { return bool(fallback_); @@ -9853,44 +9853,44 @@ class MGuardShapePolymorphic } }; // Guard on an object's group, inclusively or exclusively. class MGuardObjectGroup : public MUnaryInstruction, public SingleObjectPolicy::Data { - AlwaysTenured<types::ObjectGroup *> group_; + AlwaysTenured<ObjectGroup *> group_; bool bailOnEquality_; BailoutKind bailoutKind_; - MGuardObjectGroup(MDefinition *obj, types::ObjectGroup *group, bool bailOnEquality, + MGuardObjectGroup(MDefinition *obj, ObjectGroup *group, bool bailOnEquality, BailoutKind bailoutKind) : MUnaryInstruction(obj), group_(group), bailOnEquality_(bailOnEquality), bailoutKind_(bailoutKind) { setGuard(); setMovable(); setResultType(MIRType_Object); } public: INSTRUCTION_HEADER(GuardObjectGroup) - static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, types::ObjectGroup *group, + static MGuardObjectGroup *New(TempAllocator &alloc, MDefinition *obj, ObjectGroup *group, bool bailOnEquality, BailoutKind bailoutKind) { return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind); } MDefinition *obj() const { return getOperand(0); } - const types::ObjectGroup *group() const { + const ObjectGroup *group() const { return group_; } bool bailOnEquality() const { return bailOnEquality_; } BailoutKind bailoutKind() const { return bailoutKind_; }
--- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -149,17 +149,17 @@ class MIRGenerator uint32_t minAsmJSHeapLength() const { return minAsmJSHeapLength_; } bool modifiesFrameArguments() const { return modifiesFrameArguments_; } - typedef Vector<types::ObjectGroup *, 0, JitAllocPolicy> ObjectGroupVector; + typedef Vector<ObjectGroup *, 0, JitAllocPolicy> ObjectGroupVector; // When abortReason() == AbortReason_NewScriptProperties, all types which // the new script properties analysis hasn't been performed on yet. const ObjectGroupVector &abortedNewScriptPropertiesGroups() const { return abortedNewScriptPropertiesGroups_; } public: @@ -194,17 +194,17 @@ class MIRGenerator bool instrumentedProfilingIsCached_; // List of nursery objects used by this compilation. Can be traced by a // minor GC while compilation happens off-thread. This Vector should only // be accessed on the main thread (IonBuilder, nursery GC or // CodeGenerator::link). ObjectVector nurseryObjects_; - void addAbortedNewScriptPropertiesGroup(types::ObjectGroup *type); + void addAbortedNewScriptPropertiesGroup(ObjectGroup *type); void setForceAbort() { shouldForceAbort_ = true; } bool shouldForceAbort() { return shouldForceAbort_; } #if defined(JS_ION_PERF)
--- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -88,17 +88,17 @@ MIRGenerator::abort(const char *message, va_list ap; va_start(ap, message); abortFmt(message, ap); va_end(ap); return false; } void -MIRGenerator::addAbortedNewScriptPropertiesGroup(types::ObjectGroup *group) +MIRGenerator::addAbortedNewScriptPropertiesGroup(ObjectGroup *group) { for (size_t i = 0; i < abortedNewScriptPropertiesGroups_.length(); i++) { if (group == abortedNewScriptPropertiesGroups_[i]) return; } if (!abortedNewScriptPropertiesGroups_.append(group)) CrashAtUnhandlableOOM("addAbortedNewScriptPropertiesGroup"); }
--- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -55,17 +55,17 @@ class TypeWrapper { return 0; return 1; } inline JSObject *getSingletonNoBarrier(unsigned) const { if (t_.isSingleton()) return t_.singletonNoBarrier(); return nullptr; } - inline types::ObjectGroup *getGroupNoBarrier(unsigned) const { + inline ObjectGroup *getGroupNoBarrier(unsigned) const { if (t_.isGroup()) return t_.groupNoBarrier(); return nullptr; } }; } /* anonymous namespace */ @@ -141,18 +141,18 @@ MacroAssembler::guardTypeSet(const Sourc bind(&fail); // Type set guards might miss when an object's type changes and its // properties become unknown, so check for this case. if (obj == scratch) extractObject(address, scratch); loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); branchTestPtr(Assembler::NonZero, - Address(scratch, types::ObjectGroup::offsetOfFlags()), - Imm32(types::OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched); + Address(scratch, ObjectGroup::offsetOfFlags()), + Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), &matched); assumeUnreachable("Unexpected object type"); #endif } bind(&matched); } @@ -205,17 +205,17 @@ MacroAssembler::guardObjectType(Register for (unsigned i = 0; i < count; i++) { if (!types->getGroupNoBarrier(i)) continue; if (lastBranch.isInitialized()) lastBranch.emit(*this); - types::ObjectGroup *group = types->getGroupNoBarrier(i); + ObjectGroup *group = types->getGroupNoBarrier(i); lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(group), &matched); } } if (!lastBranch.isInitialized()) { jump(miss); return; }
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -285,22 +285,22 @@ class MacroAssembler : public MacroAssem } void loadBaseShape(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfShape()), dest); loadPtr(Address(dest, Shape::offsetOfBase()), dest); } void loadObjClass(Register objReg, Register dest) { loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest); - loadPtr(Address(dest, types::ObjectGroup::offsetOfClasp()), dest); + loadPtr(Address(dest, ObjectGroup::offsetOfClasp()), dest); } void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp, Label *label) { loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); - branchPtr(cond, Address(scratch, types::ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label); + branchPtr(cond, Address(scratch, ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label); } void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label); } void branchTestObjShape(Condition cond, Register obj, Register shape, Label *label) { branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label); } void branchTestProxyHandlerFamily(Condition cond, Register proxy, Register scratch, @@ -343,17 +343,17 @@ class MacroAssembler : public MacroAssem } void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) { loadPtr(Address(obj, NativeObject::getPrivateDataOffset(nfixed)), dest); } void loadObjProto(Register obj, Register dest) { loadPtr(Address(obj, JSObject::offsetOfGroup()), dest); - loadPtr(Address(dest, types::ObjectGroup::offsetOfProto()), dest); + loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest); } void loadStringLength(Register str, Register dest) { load32(Address(str, JSString::offsetOfLength()), dest); } void loadFunctionFromCalleeToken(Address token, Register dest) { loadPtr(token, dest);
--- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -813,17 +813,17 @@ WriteOffsetsTable(CompactBufferWriter &w return true; } static JSFunction * MaybeConstructorFromType(types::Type ty) { if (ty.isUnknown() || ty.isAnyObject() || !ty.isGroup()) return nullptr; - types::ObjectGroup *obj = ty.group(); + ObjectGroup *obj = ty.group(); types::TypeNewScript *newScript = obj->newScript(); if (!newScript && obj->maybeUnboxedLayout()) newScript = obj->unboxedLayout().newScript(); return newScript ? newScript->function() : nullptr; } static void SpewConstructor(types::Type ty, JSFunction *constructor) @@ -949,17 +949,17 @@ jit::WriteIonTrackedOptimizationsTable(J types::Type ty = uniqueTypeList[i]; if (JSFunction *constructor = MaybeConstructorFromType(ty)) { if (!allTypes->append(IonTrackedTypeWithAddendum(ty, constructor))) return false; SpewConstructor(ty, constructor); } else { JSScript *script; uint32_t offset; - if (cx->findAllocationSiteForType(ty, &script, &offset)) { + if (ObjectGroup::findAllocationSiteForType(cx, ty, &script, &offset)) { if (!allTypes->append(IonTrackedTypeWithAddendum(ty, script, offset))) return false; SpewAllocationSite(ty, script, offset); } else { if (!allTypes->append(IonTrackedTypeWithAddendum(ty))) return false; } }
--- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -6,16 +6,17 @@ #ifndef jit_Recover_h #define jit_Recover_h #include "mozilla/Attributes.h" #include "jsarray.h" +#include "jit/MIR.h" #include "jit/Snapshots.h" struct JSContext; namespace js { namespace jit { #define RECOVER_OPCODE_LIST(_) \
--- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -8,16 +8,17 @@ #include "mozilla/Vector.h" #include "jit/IonAnalysis.h" #include "jit/JitSpewer.h" #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" +#include "vm/UnboxedObject.h" namespace js { namespace jit { template <typename MemoryView> class EmulateStateOf { private:
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -1283,17 +1283,17 @@ MarkObjectFromIon(JSRuntime *rt, JSObjec void MarkShapeFromIon(JSRuntime *rt, Shape **shapep) { gc::MarkShapeUnbarriered(&rt->gc.marker, shapep, "write barrier"); } void -MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp) +MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp) { gc::MarkObjectGroupUnbarriered(&rt->gc.marker, groupp, "write barrier"); } bool ThrowUninitializedLexical(JSContext *cx) { ScriptFrameIter iter(cx);
--- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -340,17 +340,17 @@ template <> struct TypeToArgProperties<H }; template <> struct TypeToArgProperties<MutableHandleValue> { static const uint32_t result = TypeToArgProperties<Value>::result | VMFunction::ByRef; }; template <> struct TypeToArgProperties<HandleShape> { static const uint32_t result = TypeToArgProperties<Shape *>::result | VMFunction::ByRef; }; template <> struct TypeToArgProperties<HandleObjectGroup> { - static const uint32_t result = TypeToArgProperties<types::ObjectGroup *>::result | VMFunction::ByRef; + static const uint32_t result = TypeToArgProperties<ObjectGroup *>::result | VMFunction::ByRef; }; // Convert argument type to whether or not it should be passed in a float // register on platforms that have them, like x64. template <class T> struct TypeToPassInFloatReg { static const uint32_t result = 0; }; template <> struct TypeToPassInFloatReg<double> { @@ -754,17 +754,17 @@ void AssertValidStringPtr(JSContext *cx, void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym); void AssertValidValue(JSContext *cx, Value *v); #endif void MarkValueFromIon(JSRuntime *rt, Value *vp); void MarkStringFromIon(JSRuntime *rt, JSString **stringp); void MarkObjectFromIon(JSRuntime *rt, JSObject **objp); void MarkShapeFromIon(JSRuntime *rt, Shape **shapep); -void MarkObjectGroupFromIon(JSRuntime *rt, types::ObjectGroup **groupp); +void MarkObjectGroupFromIon(JSRuntime *rt, ObjectGroup **groupp); // Helper for generatePreBarrier. inline void * IonMarkFunction(MIRType type) { switch (type) { case MIRType_Value: return JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -1986,17 +1986,17 @@ CodeGeneratorX86Shared::visitGuardObject void CodeGeneratorX86Shared::visitGuardClass(LGuardClass *guard) { Register obj = ToRegister(guard->input()); Register tmp = ToRegister(guard->tempInt()); masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); - masm.cmpPtr(Operand(tmp, types::ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); + masm.cmpPtr(Operand(tmp, ObjectGroup::offsetOfClasp()), ImmPtr(guard->mir()->getClass())); bailoutIf(Assembler::NotEqual, guard->snapshot()); } void CodeGeneratorX86Shared::visitEffectiveAddress(LEffectiveAddress *ins) { const MEffectiveAddress *mir = ins->mir(); Register base = ToRegister(ins->base());
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1242,17 +1242,17 @@ enum ShouldUpdateTypes static bool InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes) { MOZ_ASSERT(count <= MAX_ARRAY_INDEX); if (count == 0) return true; - types::ObjectGroup *group = obj->getGroup(cx); + ObjectGroup *group = obj->getGroup(cx); if (!group) return false; if (updateTypes && !InitArrayTypes(cx, group, vector, count)) return false; /* * Optimize for dense arrays so long as adding the given set of elements * wouldn't otherwise make the array slow or exceed a non-writable array @@ -2321,17 +2321,17 @@ js::array_unshift(JSContext *cx, unsigne static inline void TryReuseArrayGroup(JSObject *obj, ArrayObject *narr) { /* * Try to change the group of a newly created array narr to the same group * as obj. This can only be performed if the original object is an array * and has the same prototype. */ - MOZ_ASSERT(narr->getProto()->hasNewGroup(&ArrayObject::class_, narr->group())); + MOZ_ASSERT(ObjectGroup::hasDefaultNewGroup(narr->getProto(), &ArrayObject::class_, narr->group())); if (obj->is<ArrayObject>() && !obj->isSingleton() && obj->getProto() == narr->getProto()) narr->setGroup(obj->group()); } /* * Returns true if this is a dense array whose |count| properties starting from * |startingIndex| may be accessed (get, set, delete) directly through its @@ -2358,17 +2358,17 @@ CanOptimizeForDenseStorage(HandleObject * deleted if a hole is moved from one location to another location not yet * visited. See bug 690622. * * Another potential wrinkle: what if the enumeration is happening on an * object which merely has |arr| on its prototype chain? It turns out this * case can't happen, because any dense array used as the prototype of * another object is first slowified, for type inference's sake. */ - types::ObjectGroup *arrGroup = arr->getGroup(cx); + ObjectGroup *arrGroup = arr->getGroup(cx); if (MOZ_UNLIKELY(!arrGroup || arrGroup->hasAllFlags(OBJECT_FLAG_ITERATED))) return false; /* * Now watch out for getters and setters along the prototype chain or in * other indexed properties on the object. (Note that non-writable length * is subsumed by the initializedLength comparison.) */ @@ -2977,17 +2977,17 @@ array_filter(JSContext *cx, unsigned arg /* Step 5. */ RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue()); /* Step 6. */ RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0)); if (!arr) return false; - ObjectGroup *newGroup = GetCallerInitGroup(cx, JSProto_Array); + ObjectGroup *newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array); if (!newGroup) return false; arr->setGroup(newGroup); /* Step 7. */ uint32_t k = 0; /* Step 8. */ @@ -3077,17 +3077,17 @@ ArrayFromCallArgs(JSContext *cx, HandleO static bool array_of(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) { // IsArrayConstructor(this) will usually be true in practice. This is // the most common path. - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; return ArrayFromCallArgs(cx, group, args); } // Step 4. RootedObject obj(cx); { @@ -3184,17 +3184,17 @@ static const JSFunctionSpec array_static JS_FS_END }; /* ES5 15.4.2 */ bool js_Array(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; if (args.length() != 1 || !args[0].isNumber()) return ArrayFromCallArgs(cx, group, args); uint32_t length; if (args[0].isInt32()) { @@ -3246,17 +3246,18 @@ js::ArrayConstructorOneArg(JSContext *cx static JSObject * CreateArrayPrototype(JSContext *cx, JSProtoKey key) { MOZ_ASSERT(key == JSProto_Array); RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); if (!proto) return nullptr; - RootedObjectGroup group(cx, cx->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + 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), @@ -3374,17 +3375,18 @@ NewArray(ExclusiveContext *cxArg, uint32 RootedObject proto(cxArg, protoArg); if (protoArg) JS::PoisonPtr(&protoArg); if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto)) return nullptr; - RootedObjectGroup group(cxArg, cxArg->getNewGroup(&ArrayObject::class_, TaggedProto(proto))); + RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_, + TaggedProto(proto))); if (!group) return nullptr; JSObject *metadata = nullptr; if (!NewObjectMetadata(cxArg, &metadata)) return nullptr; /*
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -111,17 +111,17 @@ JSCompartment::sweepCallsiteClones() } JSFunction * js::ExistingCloneFunctionAtCallsite(const CallsiteCloneTable &table, JSFunction *fun, JSScript *script, jsbytecode *pc) { MOZ_ASSERT(fun->nonLazyScript()->shouldCloneAtCallsite()); MOZ_ASSERT(!fun->nonLazyScript()->enclosingStaticScope()); - MOZ_ASSERT(types::UseSingletonForClone(fun)); + MOZ_ASSERT(ObjectGroup::useSingletonForClone(fun)); /* * If we start allocating function objects in the nursery, then the callsite * clone table will need a postbarrier. */ MOZ_ASSERT(fun->isTenured()); if (!table.initialized()) @@ -248,17 +248,17 @@ js::DestroyContext(JSContext *cx, Destro cx->remove(); bool last = !rt->hasContexts(); if (last) { /* * Dump remaining type inference results while we still have a context. * This printing depends on atoms still existing. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) - c->types.print(cx, false); + types::PrintTypes(cx, c, false); } if (mode == DCM_FORCE_GC) { MOZ_ASSERT(!rt->isHeapBusy()); JS::PrepareForFullGC(rt); rt->gc.gc(GC_NORMAL, JS::gcreason::DESTROY_CONTEXT); } js_delete_poison(cx); }
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -296,23 +296,16 @@ class ExclusiveContext : public ContextF } JS::Zone *zone() const { MOZ_ASSERT_IF(!compartment(), !zone_); MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_); return zone_; } // Zone local methods that can be used freely from an ExclusiveContext. - types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto, - JSObject *associated = nullptr); - types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto); - - // Returns false if not found. - bool findAllocationSiteForType(types::Type ty, JSScript **script, uint32_t *offset) const; - inline js::LifoAlloc &typeLifoAlloc(); // Current global. This is only safe to use within the scope of the // AutoCompartment from which it's called. inline js::Handle<js::GlobalObject*> global() const; // Methods to access runtime data that must be protected by locks. frontend::ParseMapPool &parseMapPool() {
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -540,23 +540,16 @@ JSCompartment::markRoots(JSTracer *trc) void JSCompartment::sweepInnerViews() { innerViews.sweep(runtimeFromAnyThread()); } void -JSCompartment::sweepObjectGroupTables() -{ - sweepNewObjectGroupTable(newObjectGroups); - sweepNewObjectGroupTable(lazyObjectGroups); -} - -void JSCompartment::sweepSavedStacks() { savedStacks_.sweep(runtimeFromAnyThread()); } void JSCompartment::sweepGlobalObject(FreeOp *fop) { @@ -647,20 +640,19 @@ JSCompartment::sweepCrossCompartmentWrap e.rekeyFront(key); } } } void JSCompartment::fixupAfterMovingGC() { fixupGlobal(); - fixupNewObjectGroupTable(newObjectGroups); - fixupNewObjectGroupTable(lazyObjectGroups); fixupInitialShapeTable(); fixupBaseShapeTable(); + objectGroups.fixupTablesAfterMovingGC(); } void JSCompartment::fixupGlobal() { GlobalObject *global = *global_.unsafeGet(); if (global) global_.set(MaybeForwarded(global)); @@ -683,25 +675,21 @@ JSCompartment::clearTables() MOZ_ASSERT(crossCompartmentWrappers.empty()); MOZ_ASSERT_IF(callsiteClones.initialized(), callsiteClones.empty()); MOZ_ASSERT(!jitCompartment_); MOZ_ASSERT(!debugScopes); MOZ_ASSERT(!gcWeakMapList); MOZ_ASSERT(enumerators->next() == enumerators); MOZ_ASSERT(regExps.empty()); - types.clearTables(); + objectGroups.clearTables(); if (baseShapes.initialized()) baseShapes.clear(); if (initialShapes.initialized()) initialShapes.clear(); - if (newObjectGroups.initialized()) - newObjectGroups.clear(); - if (lazyObjectGroups.initialized()) - lazyObjectGroups.clear(); if (savedStacks_.initialized()) savedStacks_.clear(); } void JSCompartment::setObjectMetadataCallback(js::ObjectMetadataCallback callback) { // Clear any jitcode in the runtime, which behaves differently depending on @@ -811,21 +799,20 @@ JSCompartment::addSizeOfIncludingThis(mo size_t *compartmentTables, size_t *innerViewsArg, size_t *lazyArrayBuffersArg, size_t *crossCompartmentWrappersArg, size_t *regexpCompartment, size_t *savedStacksSet) { *compartmentObject += mallocSizeOf(this); - types.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, - tiArrayTypeTables, tiObjectTypeTables); + objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables, + tiArrayTypeTables, tiObjectTypeTables, + compartmentTables); *compartmentTables += baseShapes.sizeOfExcludingThis(mallocSizeOf) - + initialShapes.sizeOfExcludingThis(mallocSizeOf) - + newObjectGroups.sizeOfExcludingThis(mallocSizeOf) - + lazyObjectGroups.sizeOfExcludingThis(mallocSizeOf); + + initialShapes.sizeOfExcludingThis(mallocSizeOf); *innerViewsArg += innerViews.sizeOfExcludingThis(mallocSizeOf); if (lazyArrayBuffers) *lazyArrayBuffersArg += lazyArrayBuffers->sizeOfIncludingThis(mallocSizeOf); *crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf); *regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf); *savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf); }
--- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -215,19 +215,16 @@ struct JSCompartment inline js::GlobalObject *maybeGlobal() const; /* An unbarriered getter for use while tracing. */ inline js::GlobalObject *unsafeUnbarrieredMaybeGlobal() const; inline void initGlobal(js::GlobalObject &global); public: - /* Type information about the scripts and objects in this compartment. */ - js::types::TypeCompartment types; - void *data; private: js::ObjectMetadataCallback objectMetadataCallback; js::SavedStacks savedStacks_; js::WrapperMap crossCompartmentWrappers; @@ -271,24 +268,20 @@ struct JSCompartment /* Set of all unowned base shapes in the compartment. */ js::BaseShapeSet baseShapes; void sweepBaseShapeTable(); /* Set of initial shapes in the compartment. */ js::InitialShapeSet initialShapes; void sweepInitialShapeTable(); - /* Set of default 'new' or lazy groups in the compartment. */ - js::types::NewObjectGroupTable newObjectGroups; - js::types::NewObjectGroupTable lazyObjectGroups; - void sweepNewObjectGroupTable(js::types::NewObjectGroupTable &table); + // Object group tables and other state in the compartment. + js::ObjectGroupCompartment objectGroups; #ifdef JSGC_HASH_TABLE_CHECKS - void checkObjectGroupTablesAfterMovingGC(); - void checkObjectGroupTableAfterMovingGC(js::types::NewObjectGroupTable &table); void checkInitialShapesTableAfterMovingGC(); void checkWrapperMapAfterMovingGC(); void checkBaseShapeTableAfterMovingGC(); #endif /* * Hash table of all manually call site-cloned functions from within * self-hosted code. Cloning according to call site provides extra @@ -381,31 +374,29 @@ struct JSCompartment }; void trace(JSTracer *trc); void markRoots(JSTracer *trc); bool preserveJitCode() { return gcPreserveJitCode; } void sweepInnerViews(); void sweepCrossCompartmentWrappers(); - void sweepObjectGroupTables(); void sweepSavedStacks(); void sweepGlobalObject(js::FreeOp *fop); void sweepSelfHostingScriptSource(); void sweepJitCompartment(js::FreeOp *fop); void sweepRegExps(); void sweepDebugScopes(); void sweepWeakMaps(); void sweepNativeIterators(); void purge(); void clearTables(); void fixupInitialShapeTable(); - void fixupNewObjectGroupTable(js::types::NewObjectGroupTable &table); void fixupAfterMovingGC(); void fixupGlobal(); void fixupBaseShapeTable(); bool hasObjectMetadataCallback() const { return objectMetadataCallback; } void setObjectMetadataCallback(js::ObjectMetadataCallback callback); void forgetObjectMetadataCallback() { objectMetadataCallback = nullptr;
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -868,17 +868,17 @@ CreateFunctionPrototype(JSContext *cx, J /* staticLevel = */ 0, sourceObject, 0, ss->length())); if (!script || !JSScript::fullyInitTrivial(cx, script)) return nullptr; functionProto->initScript(script); - types::ObjectGroup* protoGroup = functionProto->getGroup(cx); + ObjectGroup* protoGroup = functionProto->getGroup(cx); if (!protoGroup) return nullptr; protoGroup->setInterpretedFunction(functionProto); script->setFunction(functionProto); /* * The default 'new' group of Function.prototype is required by type @@ -2032,17 +2032,17 @@ js::NewFunctionWithProto(ExclusiveContex return fun; } bool js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun) { return compartment == fun->compartment() && !fun->isSingleton() && - !types::UseSingletonForClone(fun); + !ObjectGroup::useSingletonForClone(fun); } JSFunction * js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, gc::AllocKind allocKind, NewObjectKind newKindArg /* = GenericObject */) { MOZ_ASSERT(parent); MOZ_ASSERT(!fun->isBoundFunction());
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -281,17 +281,17 @@ const uint32_t Arena::ThingSizes[] = CHE sizeof(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */ sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ sizeof(JSScript), /* FINALIZE_SCRIPT */ sizeof(LazyScript), /* FINALIZE_LAZY_SCRIPT */ sizeof(Shape), /* FINALIZE_SHAPE */ sizeof(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */ - sizeof(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ + sizeof(ObjectGroup), /* FINALIZE_OBJECT_GROUP */ sizeof(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ sizeof(JSString), /* FINALIZE_STRING */ sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ sizeof(JS::Symbol), /* FINALIZE_SYMBOL */ sizeof(jit::JitCode), /* FINALIZE_JITCODE */ ); #undef CHECK_MIN_THING_SIZE_INNER @@ -312,17 +312,17 @@ const uint32_t Arena::FirstThingOffsets[ OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */ OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ OFFSET(JSScript), /* FINALIZE_SCRIPT */ OFFSET(LazyScript), /* FINALIZE_LAZY_SCRIPT */ OFFSET(Shape), /* FINALIZE_SHAPE */ OFFSET(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */ OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */ - OFFSET(types::ObjectGroup), /* FINALIZE_OBJECT_GROUP */ + OFFSET(ObjectGroup), /* FINALIZE_OBJECT_GROUP */ OFFSET(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */ OFFSET(JSString), /* FINALIZE_STRING */ OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ OFFSET(JS::Symbol), /* FINALIZE_SYMBOL */ OFFSET(jit::JitCode), /* FINALIZE_JITCODE */ }; #undef OFFSET @@ -604,17 +604,17 @@ FinalizeArenas(FreeOp *fop, return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_SHAPE: return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_ACCESSOR_SHAPE: return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_BASE_SHAPE: return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_OBJECT_GROUP: - return FinalizeTypedArenas<types::ObjectGroup>(fop, src, dest, thingKind, budget, keepArenas); + return FinalizeTypedArenas<ObjectGroup>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_STRING: return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_FAT_INLINE_STRING: return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_EXTERNAL_STRING: return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas); case FINALIZE_SYMBOL: return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget, keepArenas); @@ -2221,17 +2221,17 @@ GCRuntime::sweepTypesAfterCompacting(Zon types::AutoClearTypeInferenceStateOnOOM oom(zone); for (ZoneCellIterUnderGC i(zone, FINALIZE_SCRIPT); !i.done(); i.next()) { JSScript *script = i.get<JSScript>(); script->maybeSweepTypes(&oom); } for (ZoneCellIterUnderGC i(zone, FINALIZE_OBJECT_GROUP); !i.done(); i.next()) { - types::ObjectGroup *group = i.get<types::ObjectGroup>(); + ObjectGroup *group = i.get<ObjectGroup>(); group->maybeSweep(&oom); } zone->types.endSweep(rt); } void GCRuntime::sweepZoneAfterCompacting(Zone *zone) @@ -2242,17 +2242,17 @@ GCRuntime::sweepZoneAfterCompacting(Zone sweepTypesAfterCompacting(zone); zone->sweepBreakpoints(fop); for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { c->sweepInnerViews(); c->sweepCrossCompartmentWrappers(); c->sweepBaseShapeTable(); c->sweepInitialShapeTable(); - c->sweepObjectGroupTables(); + c->objectGroups.sweep(fop); c->sweepRegExps(); c->sweepCallsiteClones(); c->sweepSavedStacks(); c->sweepGlobalObject(fop); c->sweepSelfHostingScriptSource(); c->sweepDebugScopes(); c->sweepJitCompartment(fop); c->sweepWeakMaps(); @@ -2306,17 +2306,17 @@ UpdateCellPointers(MovingTracer *trc, Ar return; case FINALIZE_ACCESSOR_SHAPE: UpdateCellPointersTyped<AccessorShape>(trc, arena, traceKind); return; case FINALIZE_BASE_SHAPE: UpdateCellPointersTyped<BaseShape>(trc, arena, traceKind); return; case FINALIZE_OBJECT_GROUP: - UpdateCellPointersTyped<types::ObjectGroup>(trc, arena, traceKind); + UpdateCellPointersTyped<ObjectGroup>(trc, arena, traceKind); return; case FINALIZE_JITCODE: UpdateCellPointersTyped<jit::JitCode>(trc, arena, traceKind); return; default: MOZ_CRASH("Invalid alloc kind for UpdateCellPointers"); } } @@ -4867,17 +4867,17 @@ SweepInitialShapesTask::run() for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) c->sweepInitialShapeTable(); } /* virtual */ void SweepObjectGroupsTask::run() { for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) - c->sweepObjectGroupTables(); + c->objectGroups.sweep(runtime->defaultFreeOp()); } /* virtual */ void SweepRegExpsTask::run() { for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) c->sweepRegExps(); } @@ -5192,17 +5192,17 @@ SweepThing(Shape *shape) static void SweepThing(JSScript *script, types::AutoClearTypeInferenceStateOnOOM *oom) { script->maybeSweepTypes(oom); } static void -SweepThing(types::ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom) +SweepThing(ObjectGroup *group, types::AutoClearTypeInferenceStateOnOOM *oom) { group->maybeSweep(oom); } template <typename T, typename... Args> static bool SweepArenaList(ArenaHeader **arenasToSweep, SliceBudget &sliceBudget, Args... args) { @@ -5245,17 +5245,17 @@ GCRuntime::sweepPhase(SliceBudget &slice for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) { ArenaLists &al = sweepZone->arenas; types::AutoClearTypeInferenceStateOnOOM oom(sweepZone); if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate, sliceBudget, &oom)) return NotFinished; - if (!SweepArenaList<types::ObjectGroup>( + if (!SweepArenaList<ObjectGroup>( &al.gcObjectGroupArenasToUpdate, sliceBudget, &oom)) { return NotFinished; } // Finish sweeping type information in the zone. { gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_TYPES_END); @@ -6537,17 +6537,17 @@ gc::MergeCompartments(JSCompartment *sou for (ZoneCellIter iter(source->zone(), FINALIZE_BASE_SHAPE); !iter.done(); iter.next()) { BaseShape *base = iter.get<BaseShape>(); MOZ_ASSERT(base->compartment() == source); base->compartment_ = target; } for (ZoneCellIter iter(source->zone(), FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { - types::ObjectGroup *group = iter.get<types::ObjectGroup>(); + ObjectGroup *group = iter.get<ObjectGroup>(); group->setGeneration(target->zone()->types.generation); } // Fixup zone pointers in source's zone to refer to target's zone. for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { for (ArenaIter aiter(source->zone(), AllocKind(thingKind)); !aiter.done(); aiter.next()) { ArenaHeader *aheader = aiter.get(); @@ -6962,17 +6962,17 @@ JS::GCCellPtr::outOfLineKind() const void js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt) { /* * Check that internal hash tables no longer have any pointers to things * that have been moved. */ for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - c->checkObjectGroupTablesAfterMovingGC(); + c->objectGroups.checkTablesAfterMovingGC(); c->checkInitialShapesTableAfterMovingGC(); c->checkWrapperMapAfterMovingGC(); c->checkBaseShapeTableAfterMovingGC(); if (c->debugScopes) c->debugScopes->checkHashTablesAfterMovingGC(rt); } } #endif @@ -7118,17 +7118,17 @@ JS::IncrementalReferenceBarrier(GCCellPt return LazyScript::writeBarrierPre(static_cast<LazyScript*>(thing.asCell())); case JSTRACE_JITCODE: return jit::JitCode::writeBarrierPre(static_cast<jit::JitCode*>(thing.asCell())); case JSTRACE_SHAPE: return Shape::writeBarrierPre(static_cast<Shape*>(thing.asCell())); case JSTRACE_BASE_SHAPE: return BaseShape::writeBarrierPre(static_cast<BaseShape*>(thing.asCell())); case JSTRACE_OBJECT_GROUP: - return types::ObjectGroup::writeBarrierPre(static_cast<types::ObjectGroup *>(thing.asCell())); + return ObjectGroup::writeBarrierPre(static_cast<ObjectGroup *>(thing.asCell())); default: MOZ_CRASH("Invalid trace kind in IncrementalReferenceBarrier."); } } JS_PUBLIC_API(void) JS::IncrementalValueBarrier(const Value &v) {
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -63,17 +63,17 @@ enum State { /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */ template <typename T> struct MapTypeToFinalizeKind {}; template <> struct MapTypeToFinalizeKind<JSScript> { static const AllocKind kind = FINALIZE_SCRIPT; }; template <> struct MapTypeToFinalizeKind<LazyScript> { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; }; template <> struct MapTypeToFinalizeKind<Shape> { static const AllocKind kind = FINALIZE_SHAPE; }; template <> struct MapTypeToFinalizeKind<AccessorShape> { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; }; template <> struct MapTypeToFinalizeKind<BaseShape> { static const AllocKind kind = FINALIZE_BASE_SHAPE; }; -template <> struct MapTypeToFinalizeKind<types::ObjectGroup> { static const AllocKind kind = FINALIZE_OBJECT_GROUP; }; +template <> struct MapTypeToFinalizeKind<ObjectGroup> { static const AllocKind kind = FINALIZE_OBJECT_GROUP; }; template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; }; template <> struct MapTypeToFinalizeKind<JSString> { static const AllocKind kind = FINALIZE_STRING; }; template <> struct MapTypeToFinalizeKind<JSExternalString> { static const AllocKind kind = FINALIZE_EXTERNAL_STRING; }; template <> struct MapTypeToFinalizeKind<JS::Symbol> { static const AllocKind kind = FINALIZE_SYMBOL; }; template <> struct MapTypeToFinalizeKind<jit::JitCode> { static const AllocKind kind = FINALIZE_JITCODE; }; static inline bool IsNurseryAllocable(AllocKind kind)
--- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -609,20 +609,20 @@ NewGCObject(js::ExclusiveContext *cx, js template <js::AllowGC allowGC> inline jit::JitCode * NewJitCode(js::ExclusiveContext *cx) { return gc::AllocateNonObject<jit::JitCode, allowGC>(cx); } inline -types::ObjectGroup * +ObjectGroup * NewObjectGroup(js::ExclusiveContext *cx) { - return gc::AllocateNonObject<types::ObjectGroup, js::CanGC>(cx); + return gc::AllocateNonObject<ObjectGroup, js::CanGC>(cx); } template <js::AllowGC allowGC> inline JSString * NewGCString(js::ExclusiveContext *cx) { return js::gc::AllocateNonObject<JSString, allowGC>(cx); }
--- a/js/src/jshashutil.h +++ b/js/src/jshashutil.h @@ -4,16 +4,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jshashutil_h #define jshashutil_h #include "jscntxt.h" +#include "gc/Zone.h" + namespace js { /* * Used to add entries to a js::HashMap or HashSet where the key depends on a GC * thing that may be moved by generational or compacting GC between the call to * lookupForAdd() and relookupOrAdd(). */ template <class T>
--- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -220,25 +220,16 @@ types::TypeString(Type type) } const char * types::ObjectGroupString(ObjectGroup *group) { return TypeString(Type::ObjectType(group)); } -unsigned JSScript::id() { - if (!id_) { - id_ = ++compartment()->types.scriptCount; - InferSpew(ISpewOps, "script #%u: %p %s:%d", - id_, this, filename() ? filename() : "<null>", lineno()); - } - return id_; -} - void types::InferSpew(SpewChannel channel, const char *fmt, ...) { if (!InferSpewActive(channel)) return; va_list ap; va_start(ap, fmt); @@ -303,17 +294,17 @@ types::TypeFailure(JSContext *cx, const va_list ap; va_start(ap, fmt); JS_vsnprintf(errbuf, sizeof(errbuf), fmt, ap); va_end(ap); JS_snprintf(msgbuf, sizeof(msgbuf), "[infer failure] %s", errbuf); /* Dump type state, even if INFERFLAGS is unset. */ - cx->compartment()->types.print(cx, true); + PrintTypes(cx, cx->compartment(), true); MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__); MOZ_CRASH(); } ///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// @@ -2255,192 +2246,16 @@ TemporaryTypeSet::propertyNeedsBarrier(C HeapTypeSetKey property = key->property(id); if (property.needsBarrier(constraints)) return true; } return false; } -///////////////////////////////////////////////////////////////////// -// TypeCompartment -///////////////////////////////////////////////////////////////////// - -TypeCompartment::TypeCompartment() -{ - PodZero(this); -} - -ObjectGroup * -TypeCompartment::newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto, - ObjectGroupFlags initialFlags) -{ - MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); - - if (cx->isJSContext()) { - if (proto.isObject() && IsInsideNursery(proto.toObject())) - initialFlags |= OBJECT_FLAG_NURSERY_PROTO; - } - - ObjectGroup *group = js::NewObjectGroup(cx); - if (!group) - return nullptr; - new(group) ObjectGroup(clasp, proto, initialFlags); - - return group; -} - -ObjectGroup * -TypeCompartment::addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key) -{ - AutoEnterAnalysis enter(cx); - - if (!allocationSiteTable) { - allocationSiteTable = cx->new_<AllocationSiteTable>(); - if (!allocationSiteTable || !allocationSiteTable->init()) { - js_delete(allocationSiteTable); - allocationSiteTable = nullptr; - return nullptr; - } - } - - AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key); - MOZ_ASSERT(!p); - - ObjectGroup *res = nullptr; - - jsbytecode *pc = key.script->offsetToPC(key.offset); - RootedScript keyScript(cx, key.script); - - if (!res) { - RootedObject proto(cx); - if (key.kind != JSProto_Null && !GetBuiltinPrototype(cx, key.kind, &proto)) - return nullptr; - - Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); - res = newObjectGroup(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE); - if (!res) - return nullptr; - key.script = keyScript; - } - - if (JSOp(*pc) == JSOP_NEWOBJECT) { - /* - * This object is always constructed the same way and will not be - * observed by other code before all properties have been added. Mark - * all the properties as definite properties of the object. - */ - RootedObject baseobj(cx, key.script->getObject(GET_UINT32_INDEX(pc))); - - if (!res->addDefiniteProperties(cx, baseobj->lastProperty())) - return nullptr; - } - - if (!allocationSiteTable->add(p, key, res)) - return nullptr; - - return res; -} - -static inline jsid -GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) -{ - PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset)); - return IdToTypeId(NameToId(name)); -} - -bool -types::UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - /* - * Make a heuristic guess at a use of JSOP_NEW that the constructed object - * should have a fresh group. We do this when the NEW is immediately - * followed by a simple assignment to an object's .prototype field. - * This is designed to catch common patterns for subclassing in JS: - * - * function Super() { ... } - * function Sub1() { ... } - * function Sub2() { ... } - * - * Sub1.prototype = new Super(); - * Sub2.prototype = new Super(); - * - * Using distinct groups for the particular prototypes of Sub1 and - * Sub2 lets us continue to distinguish the two subclasses and any extra - * properties added to those prototype objects. - */ - if (script->isGenerator()) - return false; - if (JSOp(*pc) != JSOP_NEW) - return false; - pc += JSOP_NEW_LENGTH; - if (JSOp(*pc) == JSOP_SETPROP) { - jsid id = GetAtomId(cx, script, pc, 0); - if (id == id_prototype(cx)) - return true; - } - - return false; -} - -NewObjectKind -types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key) -{ - // The return value of this method can either be tested like a boolean or - // passed to a NewObject method. - JS_STATIC_ASSERT(GenericObject == 0); - - /* - * Objects created outside loops in global and eval scripts should have - * singleton types. For now this is only done for plain objects and typed - * arrays, but not normal arrays. - */ - - if (script->functionNonDelazifying() && !script->treatAsRunOnce()) - return GenericObject; - - if (key != JSProto_Object && - !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) && - !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray)) - { - return GenericObject; - } - - /* - * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note - * indicating their boundary. - */ - - if (!script->hasTrynotes()) - return SingletonObject; - - unsigned offset = script->pcToOffset(pc); - - JSTryNote *tn = script->trynotes()->vector; - JSTryNote *tnlimit = tn + script->trynotes()->length; - for (; tn < tnlimit; tn++) { - if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) - continue; - - unsigned startOffset = script->mainOffset() + tn->start; - unsigned endOffset = startOffset + tn->length; - - if (offset >= startOffset && offset < endOffset) - return GenericObject; - } - - return SingletonObject; -} - -NewObjectKind -types::UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp) -{ - return UseSingletonForInitializer(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); -} - static inline bool ClassCanHaveExtraProperties(const Class *clasp) { return clasp->resolve || clasp->ops.lookupProperty || clasp->ops.getProperty || IsAnyTypedArrayClass(clasp); } @@ -2480,17 +2295,17 @@ types::TypeCanHaveExtraIndexedProperties const Class *clasp = types->getKnownClass(constraints); // Note: typed arrays have indexed properties not accounted for by type // information, though these are all in bounds and will be accounted for // by JIT paths. if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp))) return true; - if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES)) + if (types->hasObjectFlags(constraints, OBJECT_FLAG_SPARSE_INDEXES)) return true; JSObject *proto = types->getCommonPrototype(constraints); if (!proto) return true; return PrototypeHasIndexedProperty(constraints, proto); } @@ -2549,23 +2364,23 @@ TypeZone::addPendingRecompile(JSContext // When one script is inlined into another the caller listens to state // changes on the callee's script, so trigger these to force recompilation // of any such callers. if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup()) ObjectStateChange(cx, script->functionNonDelazifying()->group(), false); } void -TypeCompartment::print(JSContext *cx, bool force) +types::PrintTypes(JSContext *cx, JSCompartment *comp, bool force) { #ifdef DEBUG gc::AutoSuppressGC suppressGC(cx); JSAutoRequest request(cx); - Zone *zone = compartment()->zone(); + Zone *zone = comp->zone(); AutoEnterAnalysis enter(nullptr, zone); if (!force && !InferSpewActive(ISpewResult)) return; for (gc::ZoneCellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) { RootedScript script(cx, i.get<JSScript>()); if (script->types()) @@ -2575,406 +2390,19 @@ TypeCompartment::print(JSContext *cx, bo for (gc::ZoneCellIter i(zone, gc::FINALIZE_OBJECT_GROUP); !i.done(); i.next()) { ObjectGroup *group = i.get<ObjectGroup>(); group->print(); } #endif } ///////////////////////////////////////////////////////////////////// -// TypeCompartment tables -///////////////////////////////////////////////////////////////////// - -/* - * The arrayTypeTable and objectTypeTable are per-compartment tables for making - * common groups to model the contents of large script singletons and JSON - * objects. These are vanilla Arrays and native Objects, so we distinguish the - * types of different ones by looking at the types of their properties. - * - * All singleton/JSON arrays which have the same prototype, are homogenous and - * of the same element type will share a group. All singleton/JSON objects - * which have the same shape and property types will also share a group. - * We don't try to collate arrays or objects that have type mismatches. - */ - -static inline bool -NumberTypes(Type a, Type b) -{ - return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) - && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); -} - -/* - * As for GetValueType, but requires object types to be non-singletons with - * their default prototype. These are the only values that should appear in - * arrays and objects whose type can be fixed. - */ -static inline Type -GetValueTypeForTable(const Value &v) -{ - Type type = GetValueType(v); - MOZ_ASSERT(!type.isSingleton()); - return type; -} - -struct types::ArrayTableKey : public DefaultHasher<types::ArrayTableKey> -{ - Type type; - JSObject *proto; - - ArrayTableKey() - : type(Type::UndefinedType()), proto(nullptr) - {} - - ArrayTableKey(Type type, JSObject *proto) - : type(type), proto(proto) - {} - - static inline uint32_t hash(const ArrayTableKey &v) { - return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); - } - - static inline bool match(const ArrayTableKey &v1, const ArrayTableKey &v2) { - return v1.type == v2.type && v1.proto == v2.proto; - } - - bool operator==(const ArrayTableKey& other) { - return type == other.type && proto == other.proto; - } - - bool operator!=(const ArrayTableKey& other) { - return !(*this == other); - } -}; - -void -TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx, - JSObject *obj, Type elementType) -{ - MOZ_ASSERT(cx->zone()->types.activeAnalysis); - - if (!arrayTypeTable) { - arrayTypeTable = cx->new_<ArrayTypeTable>(); - if (!arrayTypeTable || !arrayTypeTable->init()) { - arrayTypeTable = nullptr; - return; - } - } - - ArrayTableKey key(elementType, obj->getProto()); - DependentAddPtr<ArrayTypeTable> p(cx, *arrayTypeTable, key); - if (p) { - obj->setGroup(p->value()); - } else { - /* Make a new type to use for future arrays with the same elements. */ - RootedObject objProto(cx, obj->getProto()); - Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto)); - ObjectGroup *group = newObjectGroup(cx, &ArrayObject::class_, taggedProto); - if (!group) - return; - obj->setGroup(group); - - AddTypePropertyId(cx, group, JSID_VOID, elementType); - - key.proto = objProto; - (void) p.add(cx, *arrayTypeTable, key, group); - } -} - -void -TypeCompartment::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) -{ - AutoEnterAnalysis enter(cx); - - /* - * If the array is of homogenous type, pick a group which will be - * shared with all other singleton/JSON arrays of the same type. - * If the array is heterogenous, keep the existing group, which has - * unknown properties. - */ - - unsigned len = obj->getDenseInitializedLength(); - if (len == 0) - return; - - Type type = GetValueTypeForTable(obj->getDenseElement(0)); - - for (unsigned i = 1; i < len; i++) { - Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); - if (ntype != type) { - if (NumberTypes(type, ntype)) - type = Type::DoubleType(); - else - return; - } - } - - setTypeToHomogenousArray(cx, obj, type); -} - -void -types::FixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj) -{ - cx->compartment()->types.fixRestArgumentsType(cx, obj); -} - -void -TypeCompartment::fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj) -{ - AutoEnterAnalysis enter(cx); - - /* - * Tracking element types for rest argument arrays is not worth it, but we - * still want it to be known that it's a dense array. - */ - setTypeToHomogenousArray(cx, obj, Type::UnknownType()); -} - -/* - * N.B. We could also use the initial shape of the object (before its type is - * fixed) as the key in the object table, but since all references in the table - * are weak the hash entries would usually be collected on GC even if objects - * with the new type/shape are still live. - */ -struct types::ObjectTableKey -{ - jsid *properties; - uint32_t nproperties; - uint32_t nfixed; - - struct Lookup { - IdValuePair *properties; - uint32_t nproperties; - uint32_t nfixed; - - Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) - : properties(properties), nproperties(nproperties), nfixed(nfixed) - {} - }; - - static inline HashNumber hash(const Lookup &lookup) { - return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ - lookup.nproperties ^ - lookup.nfixed); - } - - static inline bool match(const ObjectTableKey &v, const Lookup &lookup) { - if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed) - return false; - for (size_t i = 0; i < lookup.nproperties; i++) { - if (lookup.properties[i].id != v.properties[i]) - return false; - } - return true; - } -}; - -struct types::ObjectTableEntry -{ - ReadBarrieredObjectGroup group; - ReadBarrieredShape shape; - Type *types; -}; - -static inline void -UpdateObjectTableEntryTypes(ExclusiveContext *cx, ObjectTableEntry &entry, - IdValuePair *properties, size_t nproperties) -{ - if (entry.group->unknownProperties()) - return; - for (size_t i = 0; i < nproperties; i++) { - Type type = entry.types[i]; - Type ntype = GetValueTypeForTable(properties[i].value); - if (ntype == type) - continue; - if (ntype.isPrimitive(JSVAL_TYPE_INT32) && - type.isPrimitive(JSVAL_TYPE_DOUBLE)) - { - /* The property types already reflect 'int32'. */ - } else { - if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) && - type.isPrimitive(JSVAL_TYPE_INT32)) - { - /* Include 'double' in the property types to avoid the update below later. */ - entry.types[i] = Type::DoubleType(); - } - AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype); - } - } -} - -void -TypeCompartment::fixObjectGroup(ExclusiveContext *cx, PlainObject *obj) -{ - AutoEnterAnalysis enter(cx); - - if (!objectTypeTable) { - objectTypeTable = cx->new_<ObjectTypeTable>(); - if (!objectTypeTable || !objectTypeTable->init()) { - js_delete(objectTypeTable); - objectTypeTable = nullptr; - return; - } - } - - /* - * Use the same group for all singleton/JSON objects with the same - * base shape, i.e. the same fields written in the same order. - * - * Exclude some objects we can't readily associate common types for based on their - * shape. Objects with metadata are excluded so that the metadata does not need to - * be included in the table lookup (the metadata object might be in the nursery). - */ - if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata()) - return; - - Vector<IdValuePair> properties(cx); - if (!properties.resize(obj->slotSpan())) - return; - - Shape *shape = obj->lastProperty(); - while (!shape->isEmptyShape()) { - IdValuePair &entry = properties[shape->slot()]; - entry.id = shape->propid(); - entry.value = obj->getSlot(shape->slot()); - shape = shape->previous(); - } - - ObjectTableKey::Lookup lookup(properties.begin(), properties.length(), obj->numFixedSlots()); - ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); - - if (p) { - MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); - MOZ_ASSERT(obj->lastProperty() == p->value().shape); - - UpdateObjectTableEntryTypes(cx, p->value(), properties.begin(), properties.length()); - obj->setGroup(p->value().group); - return; - } - - /* Make a new type to use for the object and similar future ones. */ - Rooted<TaggedProto> objProto(cx, obj->getTaggedProto()); - ObjectGroup *group = newObjectGroup(cx, &PlainObject::class_, objProto); - if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) - return; - - if (obj->isIndexed()) - group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); - - ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(properties.length())); - if (!ids) - return; - - ScopedJSFreePtr<Type> types(group->zone()->pod_calloc<Type>(properties.length())); - if (!types) - return; - - for (size_t i = 0; i < properties.length(); i++) { - ids[i] = properties[i].id; - types[i] = GetValueTypeForTable(obj->getSlot(i)); - AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]); - } - - ObjectTableKey key; - key.properties = ids; - key.nproperties = properties.length(); - key.nfixed = obj->numFixedSlots(); - MOZ_ASSERT(ObjectTableKey::match(key, lookup)); - - ObjectTableEntry entry; - entry.group.set(group); - entry.shape.set(obj->lastProperty()); - entry.types = types; - - obj->setGroup(group); - - p = objectTypeTable->lookupForAdd(lookup); - if (objectTypeTable->add(p, key, entry)) { - ids.forget(); - types.forget(); - } -} - -JSObject * -TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties) -{ - AutoEnterAnalysis enter(cx); - - if (!objectTypeTable) { - objectTypeTable = cx->new_<ObjectTypeTable>(); - if (!objectTypeTable || !objectTypeTable->init()) { - js_delete(objectTypeTable); - objectTypeTable = nullptr; - return nullptr; - } - } - - /* - * Use the object group table to allocate an object with the specified - * properties, filling in its final type and shape and failing if no cache - * entry could be found for the properties. - */ - - /* - * Filter out a few cases where we don't want to use the object group table. - * Note that if the properties contain any duplicates or dense indexes, - * the lookup below will fail as such arrays of properties cannot be stored - * in the object group table --- fixObjectGroup populates the table with - * properties read off its input object, which cannot be duplicates, and - * ignores objects with dense indexes. - */ - if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT) - return nullptr; - - gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); - size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_); - - ObjectTableKey::Lookup lookup(properties, nproperties, nfixed); - ObjectTypeTable::AddPtr p = objectTypeTable->lookupForAdd(lookup); - - if (!p) - return nullptr; - - RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind)); - if (!obj) { - cx->clearPendingException(); - return nullptr; - } - MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); - - RootedShape shape(cx, p->value().shape); - if (!NativeObject::setLastProperty(cx, obj, shape)) { - cx->clearPendingException(); - return nullptr; - } - - UpdateObjectTableEntryTypes(cx, p->value(), properties, nproperties); - - for (size_t i = 0; i < nproperties; i++) - obj->setSlot(i, properties[i].value); - - obj->setGroup(p->value().group); - return obj; -} - -///////////////////////////////////////////////////////////////////// // ObjectGroup ///////////////////////////////////////////////////////////////////// -void -ObjectGroup::setProto(JSContext *cx, TaggedProto proto) -{ - MOZ_ASSERT(singleton()); - - if (proto.isObject() && IsInsideNursery(proto.toObject())) - addFlags(OBJECT_FLAG_NURSERY_PROTO); - - setProtoUnchecked(proto); -} - static inline void UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, NativeObject *obj, Shape *shape, bool indexed) { MOZ_ASSERT(obj->isSingleton() && !obj->hasLazyGroup()); if (!shape->writable()) types->setNonWritableProperty(cx); @@ -3306,22 +2734,18 @@ ObjectGroup::detachNewScript(bool writeB // Clear the TypeNewScript from this ObjectGroup and, if it has been // analyzed, remove it from the newObjectGroups table so that it will not be // produced by calling 'new' on the associated function anymore. // The TypeNewScript is not actually destroyed. TypeNewScript *newScript = anyNewScript(); MOZ_ASSERT(newScript); if (newScript->analyzed()) { - NewObjectGroupTable &newObjectGroups = newScript->function()->compartment()->newObjectGroups; - NewObjectGroupTable::Ptr p = - newObjectGroups.lookup(NewObjectGroupTable::Lookup(nullptr, proto(), newScript->function())); - MOZ_ASSERT(p->group == this); - - newObjectGroups.remove(p); + newScript->function()->compartment()->objectGroups.removeDefaultNewGroup(nullptr, proto(), + newScript->function()); } if (this->newScript()) setAddendum(Addendum_None, nullptr, writeBarrier); else unboxedLayout().setNewScript(nullptr, writeBarrier); } @@ -3631,60 +3055,16 @@ types::FillBytecodeTypeMap(JSScript *scr bytecodeMap[added++] = script->pcToOffset(pc); if (added == script->nTypeSets()) break; } } MOZ_ASSERT(added == script->nTypeSets()); } -ArrayObject * -types::GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc) -{ - // Make sure that the template object for script/pc has a type indicating - // that the object and its copies have copy on write elements. - RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>()); - MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); - - if (obj->group()->fromAllocationSite()) { - MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); - return obj; - } - - RootedObjectGroup group(cx, TypeScript::InitGroup(cx, script, pc, JSProto_Array)); - if (!group) - return nullptr; - - group->addFlags(OBJECT_FLAG_COPY_ON_WRITE); - - // Update type information in the initializer object group. - MOZ_ASSERT(obj->slotSpan() == 0); - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - const Value &v = obj->getDenseElement(i); - AddTypePropertyId(cx, group, JSID_VOID, v); - } - - obj->setGroup(group); - return obj; -} - -ArrayObject * -types::GetCopyOnWriteObject(JSScript *script, jsbytecode *pc) -{ - // GetOrFixupCopyOnWriteObject should already have been called for - // script/pc, ensuring that the template object has a group with the - // COPY_ON_WRITE flag. We don't assert this here, due to a corner case - // where this property doesn't hold. See jsop_newarray_copyonwrite in - // IonBuilder. - ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>(); - MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); - - return obj; -} - void types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) { /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */ if (!(js_CodeSpec[*pc].format & JOF_TYPESET)) return; if (!script->hasBaselineScript()) @@ -3692,76 +3072,21 @@ types::TypeMonitorResult(JSContext *cx, AutoEnterAnalysis enter(cx); Type type = GetValueType(rval); StackTypeSet *types = TypeScript::BytecodeTypes(script, pc); if (types->hasType(type)) return; - InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s", - script->id(), script->pcToOffset(pc), TypeString(type)); + InferSpew(ISpewOps, "bytecodeType: %p %05u: %s", + script, script->pcToOffset(pc), TypeString(type)); types->addType(cx, type); } -bool -types::UseSingletonForClone(JSFunction *fun) -{ - if (!fun->isInterpreted()) - return false; - - if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite()) - return true; - - if (fun->isArrow()) - return false; - - if (fun->isSingleton()) - return false; - - /* - * When a function is being used as a wrapper for another function, it - * improves precision greatly to distinguish between different instances of - * the wrapper; otherwise we will conflate much of the information about - * the wrapped functions. - * - * An important example is the Class.create function at the core of the - * Prototype.js library, which looks like: - * - * var Class = { - * create: function() { - * return function() { - * this.initialize.apply(this, arguments); - * } - * } - * }; - * - * Each instance of the innermost function will have a different wrapped - * initialize method. We capture this, along with similar cases, by looking - * for short scripts which use both .apply and arguments. For such scripts, - * whenever creating a new instance of the function we both give that - * instance a singleton type and clone the underlying script. - */ - - uint32_t begin, end; - if (fun->hasScript()) { - if (!fun->nonLazyScript()->usesArgumentsApplyAndThis()) - return false; - begin = fun->nonLazyScript()->sourceStart(); - end = fun->nonLazyScript()->sourceEnd(); - } else { - if (!fun->lazyScript()->usesArgumentsApplyAndThis()) - return false; - begin = fun->lazyScript()->begin(); - end = fun->lazyScript()->end(); - } - - return end - begin <= 100; -} - ///////////////////////////////////////////////////////////////////// // TypeScript ///////////////////////////////////////////////////////////////////// bool JSScript::makeTypes(JSContext *cx) { MOZ_ASSERT(!types_); @@ -3776,48 +3101,48 @@ JSScript::makeTypes(JSContext *cx) return false; types_ = typeScript; setTypesGeneration(cx->zone()->types.generation); #ifdef DEBUG StackTypeSet *typeArray = typeScript->typeArray(); for (unsigned i = 0; i < nTypeSets(); i++) { - InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u %p", InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(), - i, id()); + i, this); } TypeSet *thisTypes = TypeScript::ThisTypes(this); - InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s this %p", InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(), - id()); + this); unsigned nargs = functionNonDelazifying() ? functionNonDelazifying()->nargs() : 0; for (unsigned i = 0; i < nargs; i++) { TypeSet *types = TypeScript::ArgTypes(this, i); - InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u #%u", + InferSpew(ISpewOps, "typeSet: %sT%p%s arg%u %p", InferSpewColor(types), types, InferSpewColorReset(), - i, id()); + i, this); } #endif return true; } /* static */ bool JSFunction::setTypeForScriptedFunction(ExclusiveContext *cx, HandleFunction fun, bool singleton /* = false */) { if (singleton) { if (!setSingleton(cx, fun)) return false; } else { RootedObject funProto(cx, fun->getProto()); Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto)); - ObjectGroup *group = - cx->compartment()->types.newObjectGroup(cx, &JSFunction::class_, taggedProto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, + taggedProto); if (!group) return false; fun->setGroup(group); group->setInterpretedFunction(fun); } return true; @@ -4207,32 +3532,28 @@ TypeNewScript::maybeAnalyze(JSContext *c // existing group to represent fully initialized objects with all // definite properties in the prefix shape, and make a new group to // represent partially initialized objects. MOZ_ASSERT(prefixShape->slotSpan() > templateObject()->slotSpan()); ObjectGroupFlags initialFlags = group->flags() & OBJECT_FLAG_DYNAMIC_MASK; Rooted<TaggedProto> protoRoot(cx, group->proto()); - ObjectGroup *initialGroup = - cx->compartment()->types.newObjectGroup(cx, group->clasp(), protoRoot, initialFlags); + ObjectGroup *initialGroup = ObjectGroupCompartment::makeGroup(cx, group->clasp(), protoRoot, + initialFlags); if (!initialGroup) return false; if (!initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty())) return false; if (!group->addDefiniteProperties(cx, prefixShape)) return false; - NewObjectGroupTable &table = cx->compartment()->newObjectGroups; - NewObjectGroupTable::Lookup lookup(nullptr, group->proto(), function()); - - MOZ_ASSERT(table.lookup(lookup)->group == group); - table.remove(lookup); - table.putNew(lookup, NewObjectGroupEntry(initialGroup, function())); + cx->compartment()->objectGroups.replaceDefaultNewGroup(nullptr, group->proto(), function(), + initialGroup); templateObject()->setGroup(initialGroup); // Transfer this TypeNewScript from the fully initialized group to the // partially initialized group. group->setNewScript(nullptr); initialGroup->setNewScript(this); @@ -4363,391 +3684,16 @@ TypeNewScript::trace(JSTracer *trc) void TypeNewScript::sweep() { if (preliminaryObjects) preliminaryObjects->sweep(); } ///////////////////////////////////////////////////////////////////// -// JSObject -///////////////////////////////////////////////////////////////////// - -bool -JSObject::shouldSplicePrototype(JSContext *cx) -{ - /* - * During bootstrapping, if inference is enabled we need to make sure not - * to splice a new prototype in for Function.prototype or the global - * object if their __proto__ had previously been set to null, as this - * will change the prototype for all other objects with the same type. - */ - if (getProto() != nullptr) - return false; - return isSingleton(); -} - -bool -JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto) -{ - MOZ_ASSERT(cx->compartment() == compartment()); - - RootedObject self(cx, this); - - /* - * For singleton groups representing only a single JSObject, the proto - * can be rearranged as needed without destroying type information for - * the old or new types. - */ - MOZ_ASSERT(self->isSingleton()); - - // Inner objects may not appear on prototype chains. - MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); - - if (proto.isObject() && !proto.toObject()->setDelegate(cx)) - return false; - - // Force type instantiation when splicing lazy group. - RootedObjectGroup group(cx, self->getGroup(cx)); - if (!group) - return false; - RootedObjectGroup protoGroup(cx, nullptr); - if (proto.isObject()) { - protoGroup = proto.toObject()->getGroup(cx); - if (!protoGroup) - return false; - } - - group->setClasp(clasp); - group->setProto(cx, proto); - return true; -} - -/* static */ ObjectGroup * -JSObject::makeLazyGroup(JSContext *cx, HandleObject obj) -{ - MOZ_ASSERT(obj->hasLazyGroup()); - MOZ_ASSERT(cx->compartment() == obj->compartment()); - - /* De-lazification of functions can GC, so we need to do it up here. */ - if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) { - RootedFunction fun(cx, &obj->as<JSFunction>()); - if (!fun->getOrCreateScript(cx)) - return nullptr; - } - - // Find flags which need to be specified immediately on the object. - // Don't track whether singletons are packed. - ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED; - - if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) - initialFlags |= OBJECT_FLAG_ITERATED; - - if (obj->isIndexed()) - initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; - - if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX) - initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; - - Rooted<TaggedProto> proto(cx, obj->getTaggedProto()); - ObjectGroup *group = cx->compartment()->types.newObjectGroup(cx, obj->getClass(), proto, - initialFlags); - if (!group) - return nullptr; - - AutoEnterAnalysis enter(cx); - - /* Fill in the type according to the state of this object. */ - - group->initSingleton(obj); - - if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) - group->setInterpretedFunction(&obj->as<JSFunction>()); - - obj->group_ = group; - - return group; -} - -/* static */ inline HashNumber -NewObjectGroupEntry::hash(const Lookup &lookup) -{ - return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^ - PointerHasher<const Class *, 3>::hash(lookup.clasp) ^ - PointerHasher<JSObject *, 3>::hash(lookup.associated); -} - -/* static */ inline bool -NewObjectGroupEntry::match(const NewObjectGroupEntry &key, const Lookup &lookup) -{ - return key.group->proto() == lookup.matchProto && - (!lookup.clasp || key.group->clasp() == lookup.clasp) && - key.associated == lookup.associated; -} - -#ifdef DEBUG -bool -JSObject::hasNewGroup(const Class *clasp, ObjectGroup *group) -{ - NewObjectGroupTable &table = compartment()->newObjectGroups; - - if (!table.initialized()) - return false; - - NewObjectGroupTable::Ptr p = - table.lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(this), nullptr)); - return p && p->group == group; -} -#endif /* DEBUG */ - -/* static */ bool -JSObject::setNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj) -{ - if (!obj->setFlag(cx, js::BaseShape::NEW_GROUP_UNKNOWN)) - return false; - - // If the object already has a new group, mark that group as unknown. - NewObjectGroupTable &table = cx->compartment()->newObjectGroups; - if (table.initialized()) { - Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj)); - NewObjectGroupTable::Ptr p = - table.lookup(NewObjectGroupTable::Lookup(clasp, taggedProto, nullptr)); - if (p) - MarkObjectGroupUnknownProperties(cx, p->group); - } - - return true; -} - -/* - * This class is used to add a post barrier on the newObjectGroups set, as the - * key is calculated from a prototype object which may be moved by generational - * GC. - */ -class NewObjectGroupsSetRef : public BufferableRef -{ - NewObjectGroupTable *set; - const Class *clasp; - JSObject *proto; - JSObject *associated; - - public: - NewObjectGroupsSetRef(NewObjectGroupTable *s, const Class *clasp, JSObject *proto, JSObject *associated) - : set(s), clasp(clasp), proto(proto), associated(associated) - {} - - void mark(JSTracer *trc) { - JSObject *prior = proto; - trc->setTracingLocation(&*prior); - Mark(trc, &proto, "newObjectGroups set prototype"); - if (prior == proto) - return; - - NewObjectGroupTable::Ptr p = - set->lookup(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), - associated)); - if (!p) - return; - - set->rekeyAs(NewObjectGroupTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), - NewObjectGroupTable::Lookup(clasp, TaggedProto(proto), associated), *p); - } -}; - -static void -ObjectGroupTablePostBarrier(ExclusiveContext *cx, NewObjectGroupTable *table, - const Class *clasp, TaggedProto proto, JSObject *associated) -{ - MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); - - if (!proto.isObject()) - return; - - if (!cx->isJSContext()) { - MOZ_ASSERT(!IsInsideNursery(proto.toObject())); - return; - } - - if (IsInsideNursery(proto.toObject())) { - StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; - sb.putGeneric(NewObjectGroupsSetRef(table, clasp, proto.toObject(), associated)); - } -} - -ObjectGroup * -ExclusiveContext::getNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated) -{ - MOZ_ASSERT_IF(associated, proto.isObject()); - MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>()); - MOZ_ASSERT_IF(proto.isObject(), isInsideCurrentCompartment(proto.toObject())); - - // A null lookup clasp is used for 'new' groups with an associated - // function. The group starts out as a plain object but might mutate into an - // unboxed plain object. - MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>())); - - NewObjectGroupTable &newObjectGroups = compartment()->newObjectGroups; - - if (!newObjectGroups.initialized() && !newObjectGroups.init()) - return nullptr; - - if (associated && associated->is<JSFunction>()) { - MOZ_ASSERT(!clasp); - - // Canonicalize new functions to use the original one associated with its script. - JSFunction *fun = &associated->as<JSFunction>(); - if (fun->hasScript()) - associated = fun->nonLazyScript()->functionNonDelazifying(); - else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) - associated = fun->lazyScript()->functionNonDelazifying(); - else - associated = nullptr; - - // If we have previously cleared the 'new' script information for this - // function, don't try to construct another one. - if (associated && associated->wasNewScriptCleared()) - associated = nullptr; - - if (!associated) - clasp = &PlainObject::class_; - } - - NewObjectGroupTable::AddPtr p = - newObjectGroups.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, associated)); - if (p) { - ObjectGroup *group = p->group; - MOZ_ASSERT_IF(clasp, group->clasp() == clasp); - MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || - group->clasp() == &UnboxedPlainObject::class_); - MOZ_ASSERT(group->proto() == proto); - return group; - } - - AutoEnterAnalysis enter(this); - - if (proto.isObject() && !proto.toObject()->setDelegate(this)) - return nullptr; - - ObjectGroupFlags initialFlags = 0; - if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) - initialFlags = OBJECT_FLAG_DYNAMIC_MASK; - - Rooted<TaggedProto> protoRoot(this, proto); - ObjectGroup *group = compartment()->types.newObjectGroup(this, - clasp ? clasp : &PlainObject::class_, - protoRoot, initialFlags); - if (!group) - return nullptr; - - if (!newObjectGroups.add(p, NewObjectGroupEntry(group, associated))) - return nullptr; - - ObjectGroupTablePostBarrier(this, &newObjectGroups, clasp, proto, associated); - - if (proto.isObject()) { - RootedObject obj(this, proto.toObject()); - - if (associated) { - if (associated->is<JSFunction>()) - TypeNewScript::make(asJSContext(), group, &associated->as<JSFunction>()); - else - group->setTypeDescr(&associated->as<TypeDescr>()); - } - - /* - * Some builtin objects have slotful native properties baked in at - * creation via the Shape::{insert,get}initialShape mechanism. Since - * these properties are never explicitly defined on new objects, update - * the type information for them here. - */ - - if (obj->is<RegExpObject>()) { - AddTypePropertyId(this, group, NameToId(names().source), Type::StringType()); - AddTypePropertyId(this, group, NameToId(names().global), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().ignoreCase), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().multiline), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().sticky), Type::BooleanType()); - AddTypePropertyId(this, group, NameToId(names().lastIndex), Type::Int32Type()); - } - - if (obj->is<StringObject>()) - AddTypePropertyId(this, group, NameToId(names().length), Type::Int32Type()); - - if (obj->is<ErrorObject>()) { - AddTypePropertyId(this, group, NameToId(names().fileName), Type::StringType()); - AddTypePropertyId(this, group, NameToId(names().lineNumber), Type::Int32Type()); - AddTypePropertyId(this, group, NameToId(names().columnNumber), Type::Int32Type()); - AddTypePropertyId(this, group, NameToId(names().stack), Type::StringType()); - } - } - - return group; -} - -ObjectGroup * -ExclusiveContext::getLazySingletonGroup(const Class *clasp, TaggedProto proto) -{ - MOZ_ASSERT_IF(proto.isObject(), compartment() == proto.toObject()->compartment()); - - AutoEnterAnalysis enter(this); - - NewObjectGroupTable &table = compartment()->lazyObjectGroups; - - if (!table.initialized() && !table.init()) - return nullptr; - - NewObjectGroupTable::AddPtr p = table.lookupForAdd(NewObjectGroupTable::Lookup(clasp, proto, nullptr)); - if (p) { - ObjectGroup *group = p->group; - MOZ_ASSERT(group->lazy()); - - return group; - } - - Rooted<TaggedProto> protoRoot(this, proto); - ObjectGroup *group = compartment()->types.newObjectGroup(this, clasp, protoRoot); - if (!group) - return nullptr; - - if (!table.add(p, NewObjectGroupEntry(group, nullptr))) - return nullptr; - - ObjectGroupTablePostBarrier(this, &table, clasp, proto, nullptr); - - group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON); - MOZ_ASSERT(group->singleton(), "created group must be a proper singleton"); - - return group; -} - -bool -ExclusiveContext::findAllocationSiteForType(Type type, JSScript **script, uint32_t *offset) const -{ - *script = nullptr; - *offset = 0; - - if (type.isUnknown() || type.isAnyObject() || !type.isGroup()) - return false; - ObjectGroup *obj = type.group(); - - const AllocationSiteTable *table = compartment()->types.allocationSiteTable; - if (!table) - return false; - - for (AllocationSiteTable::Range r = table->all(); !r.empty(); r.popFront()) { - if (obj == r.front().value()) { - *script = r.front().key().script; - *offset = r.front().key().offset; - return true; - } - } - return false; -} - -///////////////////////////////////////////////////////////////////// // Tracing ///////////////////////////////////////////////////////////////////// void ConstraintTypeSet::sweep(Zone *zone, AutoClearTypeInferenceStateOnOOM &oom) { MOZ_ASSERT(zone->isGCSweepingOrCompacting()); @@ -4950,219 +3896,16 @@ ObjectGroup::maybeSweep(AutoClearTypeInf addFlags(OBJECT_FLAG_DYNAMIC_MASK | OBJECT_FLAG_UNKNOWN_PROPERTIES); clearProperties(); return; } } } } -void -TypeCompartment::clearTables() -{ - if (allocationSiteTable && allocationSiteTable->initialized()) - allocationSiteTable->clear(); - if (arrayTypeTable && arrayTypeTable->initialized()) - arrayTypeTable->clear(); - if (objectTypeTable && objectTypeTable->initialized()) - objectTypeTable->clear(); -} - -void -TypeCompartment::sweep(FreeOp *fop) -{ - /* - * Iterate through the array/object group tables and remove all entries - * referencing collected data. These tables only hold weak references. - */ - - if (arrayTypeTable) { - for (ArrayTypeTable::Enum e(*arrayTypeTable); !e.empty(); e.popFront()) { - ArrayTableKey key = e.front().key(); - MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton()); - - bool remove = false; - if (!key.type.isUnknown() && key.type.isGroup()) { - ObjectGroup *group = key.type.groupNoBarrier(); - if (IsObjectGroupAboutToBeFinalized(&group)) - remove = true; - else - key.type = Type::ObjectType(group); - } - if (key.proto && key.proto != TaggedProto::LazyProto && - IsObjectAboutToBeFinalized(&key.proto)) - { - remove = true; - } - if (IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet())) - remove = true; - - if (remove) - e.removeFront(); - else if (key != e.front().key()) - e.rekeyFront(key); - } - } - - if (objectTypeTable) { - for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) { - const ObjectTableKey &key = e.front().key(); - ObjectTableEntry &entry = e.front().value(); - - bool remove = false; - if (IsObjectGroupAboutToBeFinalized(entry.group.unsafeGet())) - remove = true; - if (IsShapeAboutToBeFinalized(entry.shape.unsafeGet())) - remove = true; - for (unsigned i = 0; !remove && i < key.nproperties; i++) { - if (JSID_IS_STRING(key.properties[i])) { - JSString *str = JSID_TO_STRING(key.properties[i]); - if (IsStringAboutToBeFinalized(&str)) - remove = true; - MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]); - } else if (JSID_IS_SYMBOL(key.properties[i])) { - JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]); - if (IsSymbolAboutToBeFinalized(&sym)) - remove = true; - } - - MOZ_ASSERT(!entry.types[i].isSingleton()); - ObjectGroup *group = nullptr; - if (entry.types[i].isGroup()) { - group = entry.types[i].groupNoBarrier(); - if (IsObjectGroupAboutToBeFinalized(&group)) - remove = true; - else if (group != entry.types[i].groupNoBarrier()) - entry.types[i] = Type::ObjectType(group); - } - } - - if (remove) { - js_free(key.properties); - js_free(entry.types); - e.removeFront(); - } - } - } - - if (allocationSiteTable) { - for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { - AllocationSiteKey key = e.front().key(); - bool keyDying = IsScriptAboutToBeFinalized(&key.script); - bool valDying = IsObjectGroupAboutToBeFinalized(e.front().value().unsafeGet()); - if (keyDying || valDying) - e.removeFront(); - else if (key.script != e.front().key().script) - e.rekeyFront(key); - } - } -} - -void -JSCompartment::sweepNewObjectGroupTable(NewObjectGroupTable &table) -{ - MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting()); - if (table.initialized()) { - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) || - (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated))) - { - e.removeFront(); - } else { - /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ - MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); - MOZ_ASSERT(entry.associated == e.front().associated); - } - } - } -} - -void -JSCompartment::fixupNewObjectGroupTable(NewObjectGroupTable &table) -{ - /* - * Each entry's hash depends on the object's prototype and we can't tell - * whether that has been moved or not in sweepNewObjectGroupTable(). - */ - MOZ_ASSERT(zone()->isCollecting()); - if (table.initialized()) { - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - bool needRekey = false; - if (IsForwarded(entry.group.get())) { - entry.group.set(Forwarded(entry.group.get())); - needRekey = true; - } - TaggedProto proto = entry.group->proto(); - if (proto.isObject() && IsForwarded(proto.toObject())) { - proto = TaggedProto(Forwarded(proto.toObject())); - needRekey = true; - } - if (entry.associated && IsForwarded(entry.associated)) { - entry.associated = Forwarded(entry.associated); - needRekey = true; - } - if (needRekey) { - const Class *clasp = entry.group->clasp(); - if (entry.associated && entry.associated->is<JSFunction>()) - clasp = nullptr; - NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); - e.rekeyFront(lookup, entry); - } - } - } -} - -#ifdef JSGC_HASH_TABLE_CHECKS - -void -JSCompartment::checkObjectGroupTablesAfterMovingGC() -{ - checkObjectGroupTableAfterMovingGC(newObjectGroups); - checkObjectGroupTableAfterMovingGC(lazyObjectGroups); -} - -void -JSCompartment::checkObjectGroupTableAfterMovingGC(NewObjectGroupTable &table) -{ - /* - * Assert that nothing points into the nursery or needs to be relocated, and - * that the hash table entries are discoverable. - */ - if (!table.initialized()) - return; - - for (NewObjectGroupTable::Enum e(table); !e.empty(); e.popFront()) { - NewObjectGroupEntry entry = e.front(); - CheckGCThingAfterMovingGC(entry.group.get()); - TaggedProto proto = entry.group->proto(); - if (proto.isObject()) - CheckGCThingAfterMovingGC(proto.toObject()); - CheckGCThingAfterMovingGC(entry.associated); - - const Class *clasp = entry.group->clasp(); - if (entry.associated && entry.associated->is<JSFunction>()) - clasp = nullptr; - - NewObjectGroupTable::Lookup lookup(clasp, proto, entry.associated); - NewObjectGroupTable::Ptr ptr = table.lookup(lookup); - MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); - } -} - -#endif // JSGC_HASH_TABLE_CHECKS - -TypeCompartment::~TypeCompartment() -{ - js_delete(arrayTypeTable); - js_delete(objectTypeTable); - js_delete(allocationSiteTable); -} - /* static */ void JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM *oom) { if (!types_ || typesGeneration() == zone()->types.generation) return; setTypesGeneration(zone()->types.generation); @@ -5216,55 +3959,16 @@ Zone::addSizeOfIncludingThis(mozilla::Ma { *typePool += types.typeLifoAlloc.sizeOfExcludingThis(mallocSizeOf); if (jitZone()) { *baselineStubsOptimized += jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf); } } -void -TypeCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *allocationSiteTables, - size_t *arrayTypeTables, - size_t *objectTypeTables) -{ - if (allocationSiteTable) - *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); - - if (arrayTypeTable) - *arrayTypeTables += arrayTypeTable->sizeOfIncludingThis(mallocSizeOf); - - if (objectTypeTable) { - *objectTypeTables += objectTypeTable->sizeOfIncludingThis(mallocSizeOf); - - for (ObjectTypeTable::Enum e(*objectTypeTable); - !e.empty(); - e.popFront()) - { - const ObjectTableKey &key = e.front().key(); - const ObjectTableEntry &value = e.front().value(); - - /* key.ids and values.types have the same length. */ - *objectTypeTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types); - } - } -} - -size_t -ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const -{ - size_t n = 0; - if (TypeNewScript *newScript = newScriptDontCheckGeneration()) - n += newScript->sizeOfIncludingThis(mallocSizeOf); - if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration()) - n += layout->sizeOfIncludingThis(mallocSizeOf); - return n; -} - TypeZone::TypeZone(Zone *zone) : zone_(zone), typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), generation(0), compilerOutputs(nullptr), sweepTypeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), sweepCompilerOutputs(nullptr), sweepReleaseTypes(false), @@ -5320,19 +4024,16 @@ TypeZone::beginSweep(FreeOp *fop, bool r sweepCompilerOutputs = compilerOutputs; compilerOutputs = newCompilerOutputs; } // All existing RecompileInfos are stale and will be updated to the new // compiler outputs list later during the sweep. Don't worry about overflow // here, since stale indexes will persist only until the sweep finishes. generation++; - - for (CompartmentsInZoneIter comp(zone()); !comp.done(); comp.next()) - comp->types.sweep(fop); } void TypeZone::endSweep(JSRuntime *rt) { js_delete(sweepCompilerOutputs); sweepCompilerOutputs = nullptr; @@ -5374,17 +4075,17 @@ TypeScript::printTypes(JSContext *cx, Ha AutoEnterAnalysis enter(nullptr, script->zone()); if (script->functionNonDelazifying()) fprintf(stderr, "Function"); else if (script->isForEval()) fprintf(stderr, "Eval"); else fprintf(stderr, "Main"); - fprintf(stderr, " #%u %s:%d ", script->id(), script->filename(), (int) script->lineno()); + fprintf(stderr, " %p %s:%d ", script.get(), script->filename(), (int) script->lineno()); if (script->functionNonDelazifying()) { if (js::PropertyName *name = script->functionNonDelazifying()->name()) name->dumpCharsNoNewline(); } fprintf(stderr, "\n this:"); TypeScript::ThisTypes(script)->print(); @@ -5395,17 +4096,17 @@ TypeScript::printTypes(JSContext *cx, Ha { fprintf(stderr, "\n arg%u:", i); TypeScript::ArgTypes(script, i)->print(); } fprintf(stderr, "\n"); for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) { { - fprintf(stderr, "#%u:", script->id()); + fprintf(stderr, "%p:", script.get()); Sprinter sprinter(cx); if (!sprinter.init()) return; js_Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter); fprintf(stderr, "%s", sprinter.string()); } if (js_CodeSpec[*pc].format & JOF_TYPESET) { @@ -5414,28 +4115,8 @@ TypeScript::printTypes(JSContext *cx, Ha types->print(); fprintf(stderr, "\n"); } } fprintf(stderr, "\n"); } #endif /* DEBUG */ - -void -ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */) -{ - MOZ_ASSERT(!needsSweep()); - MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT)); - - if (writeBarrier) { - // Manually trigger barriers if we are clearing a TypeNewScript. Other - // kinds of addendums are immutable. - if (newScript()) - TypeNewScript::writeBarrierPre(newScript()); - else - MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind); - } - - flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK; - flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT; - addendum_ = addendum; -}
--- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -18,110 +18,20 @@ #include "ds/IdValuePair.h" #include "ds/LifoAlloc.h" #include "gc/Barrier.h" #include "gc/Marking.h" #include "jit/IonTypes.h" #include "js/UbiNode.h" #include "js/Utility.h" #include "js/Vector.h" +#include "vm/ObjectGroup.h" namespace js { -class TypeDescr; -class UnboxedLayout; - -class TaggedProto -{ - public: - static JSObject * const LazyProto; - - TaggedProto() : proto(nullptr) {} - explicit TaggedProto(JSObject *proto) : proto(proto) {} - - uintptr_t toWord() const { return uintptr_t(proto); } - - bool isLazy() const { - return proto == LazyProto; - } - bool isObject() const { - /* Skip nullptr and LazyProto. */ - return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); - } - JSObject *toObject() const { - MOZ_ASSERT(isObject()); - return proto; - } - JSObject *toObjectOrNull() const { - MOZ_ASSERT(!proto || isObject()); - return proto; - } - JSObject *raw() const { return proto; } - - bool operator ==(const TaggedProto &other) { return proto == other.proto; } - bool operator !=(const TaggedProto &other) { return proto != other.proto; } - - private: - JSObject *proto; -}; - -template <> -struct RootKind<TaggedProto> -{ - static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } -}; - -template <> struct GCMethods<const TaggedProto> -{ - static TaggedProto initial() { return TaggedProto(); } - static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } -}; - -template <> struct GCMethods<TaggedProto> -{ - static TaggedProto initial() { return TaggedProto(); } - static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } -}; - -template<class Outer> -class TaggedProtoOperations -{ - const TaggedProto *value() const { - return static_cast<const Outer*>(this)->extract(); - } - - public: - uintptr_t toWord() const { return value()->toWord(); } - inline bool isLazy() const { return value()->isLazy(); } - inline bool isObject() const { return value()->isObject(); } - inline JSObject *toObject() const { return value()->toObject(); } - inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } - JSObject *raw() const { return value()->raw(); } -}; - -template <> -class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto> > -{ - friend class TaggedProtoOperations<Handle<TaggedProto> >; - const TaggedProto * extract() const { - return static_cast<const Handle<TaggedProto>*>(this)->address(); - } -}; - -template <> -class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto> > -{ - friend class TaggedProtoOperations<Rooted<TaggedProto> >; - const TaggedProto *extract() const { - return static_cast<const Rooted<TaggedProto> *>(this)->address(); - } -}; - -class CallObject; - namespace jit { struct IonScript; class JitAllocPolicy; class TempAllocator; } namespace types { @@ -346,95 +256,16 @@ enum : uint32_t { * If the property is definite, mask and shift storing the slot + 1. * Otherwise these bits are clear. */ TYPE_FLAG_DEFINITE_MASK = 0xfffc0000, TYPE_FLAG_DEFINITE_SHIFT = 18 }; typedef uint32_t TypeFlags; -/* Flags and other state stored in ObjectGroup::flags */ -enum : uint32_t { - /* Whether this group is associated with some allocation site. */ - OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, - - /* - * If set, the object's prototype might be in the nursery and can't be - * used during Ion compilation (which may be occurring off thread). - */ - OBJECT_FLAG_NURSERY_PROTO = 0x2, - - /* Mask/shift for the number of properties in propertySet */ - OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8, - OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3, - OBJECT_FLAG_PROPERTY_COUNT_LIMIT = - OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, - - /* Whether any objects this represents may have sparse indexes. */ - OBJECT_FLAG_SPARSE_INDEXES = 0x00010000, - - /* Whether any objects this represents may not have packed dense elements. */ - OBJECT_FLAG_NON_PACKED = 0x00020000, - - /* - * Whether any objects this represents may be arrays whose length does not - * fit in an int32. - */ - OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000, - - /* Whether any objects have been iterated over. */ - OBJECT_FLAG_ITERATED = 0x00080000, - - /* For a global object, whether flags were set on the RegExpStatics. */ - OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000, - - /* - * For the function on a run-once script, whether the function has actually - * run multiple times. - */ - OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000, - - /* - * For a global object, whether any array buffers in this compartment with - * typed object views have been neutered. - */ - OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000, - - /* - * Whether objects with this type should be allocated directly in the - * tenured heap. - */ - OBJECT_FLAG_PRE_TENURE = 0x00800000, - - /* Whether objects with this type might have copy on write elements. */ - OBJECT_FLAG_COPY_ON_WRITE = 0x01000000, - - /* Whether this type has had its 'new' script cleared in the past. */ - OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000, - - /* - * Whether all properties of this object are considered unknown. - * If set, all other flags in DYNAMIC_MASK will also be set. - */ - OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000, - - /* Flags which indicate dynamic properties of represented objects. */ - OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000, - - // Mask/shift for the kind of addendum attached to this group. - OBJECT_FLAG_ADDENDUM_MASK = 0x38000000, - OBJECT_FLAG_ADDENDUM_SHIFT = 27, - - // Mask/shift for this group's generation. If out of sync with the - // TypeZone's generation, this group hasn't been swept yet. - OBJECT_FLAG_GENERATION_MASK = 0x40000000, - OBJECT_FLAG_GENERATION_SHIFT = 30, -}; -typedef uint32_t ObjectGroupFlags; - class StackTypeSet; class HeapTypeSet; class TemporaryTypeSet; /* * Information about the set of types associated with an lvalue. There are * three kinds of type sets: * @@ -642,18 +473,16 @@ class HeapTypeSet : public ConstraintTyp /* Mark this type set as representing a non-writable property. */ inline void setNonWritableProperty(ExclusiveContext *cx); // Mark this type set as being non-constant. inline void setNonConstantProperty(ExclusiveContext *cx); }; -class CompilerConstraintList; - CompilerConstraintList * NewCompilerConstraintList(jit::TempAllocator &alloc); class TemporaryTypeSet : public TypeSet { public: TemporaryTypeSet() {} TemporaryTypeSet(LifoAlloc *alloc, Type type); @@ -768,40 +597,16 @@ class TemporaryTypeSet : public TypeSet bool AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, ObjectGroup *group, HandleId id); bool AddClearDefiniteFunctionUsesInScript(JSContext *cx, ObjectGroup *group, JSScript *script, JSScript *calleeScript); -/* Is this a reasonable PC to be doing inlining on? */ -inline bool isInlinableCall(jsbytecode *pc); - -/* Type information about a property. */ -struct Property -{ - /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ - HeapId id; - - /* Possible types for this property, including types inherited from prototypes. */ - HeapTypeSet types; - - explicit Property(jsid id) - : id(id) - {} - - Property(const Property &o) - : id(o.id.get()), types(o.types) - {} - - static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } - static jsid getKey(Property *p) { return p->id; } -}; - // For types where only a small number of objects have been allocated, this // structure keeps track of all objects with the type in existence. Once // COUNT objects have been allocated, this structure is cleared and the objects // are analyzed, to perform the new script properties analyses or determine if // an unboxed representation can be used. class PreliminaryObjectArray { public: @@ -959,430 +764,78 @@ class TypeNewScript bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group); static void make(JSContext *cx, ObjectGroup *group, JSFunction *fun); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; -/* - * Lazy object groups overview. - * - * Object groups which represent at most one JS object are constructed lazily. - * These include groups for native functions, standard classes, scripted - * functions defined at the top level of global/eval scripts, and in some - * other cases. Typical web workloads often create many windows (and many - * copies of standard natives) and many scripts, with comparatively few - * non-singleton groups. - * - * We can recover the type information for the object from examining it, - * so don't normally track the possible types of its properties as it is - * updated. Property type sets for the object are only constructed when an - * analyzed script attaches constraints to it: the script is querying that - * property off the object or another which delegates to it, and the analysis - * information is sensitive to changes in the property's type. Future changes - * to the property (whether those uncovered by analysis or those occurring - * in the VM) will treat these properties like those of any other object group. - */ - -/* Type information about an object accessed by a script. */ -struct ObjectGroup : public gc::TenuredCell -{ - private: - /* Class shared by objects in this group. */ - const Class *clasp_; - - /* Prototype shared by objects in this group. */ - HeapPtrObject proto_; - - /* - * Whether there is a singleton JS object with this group. That JS object - * must appear in type sets instead of this; we include the back reference - * here to allow reverting the JS object to a lazy group. - */ - HeapPtrObject singleton_; - - public: - - const Class *clasp() const { - return clasp_; - } - - void setClasp(const Class *clasp) { - clasp_ = clasp; - } - - TaggedProto proto() const { - return TaggedProto(proto_); - } - - JSObject *singleton() const { - return singleton_; - } - - // For use during marking, don't call otherwise. - HeapPtrObject &protoRaw() { return proto_; } - HeapPtrObject &singletonRaw() { return singleton_; } - - void setProto(JSContext *cx, TaggedProto proto); - void setProtoUnchecked(TaggedProto proto) { - proto_ = proto.raw(); - } - - void initSingleton(JSObject *singleton) { - singleton_ = singleton; - } - - /* - * Value held by singleton if this is a standin group for a singleton JS - * object whose group has not been constructed yet. - */ - static const size_t LAZY_SINGLETON = 1; - bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } - - private: - /* Flags for this group. */ - ObjectGroupFlags flags_; - - // Kinds of addendums which can be attached to ObjectGroups. - enum AddendumKind { - Addendum_None, - - // When used by interpreted function, the addendum stores the - // canonical JSFunction object. - Addendum_InterpretedFunction, - - // When used by the 'new' group when constructing an interpreted - // function, the addendum stores a TypeNewScript. - Addendum_NewScript, - - // When objects in this group have an unboxed representation, the - // addendum stores an UnboxedLayout (which might have a TypeNewScript - // as well, if the group is also constructed using 'new'). - Addendum_UnboxedLayout, - - // When used by typed objects, the addendum stores a TypeDescr. - Addendum_TypeDescr - }; - - // If non-null, holds additional information about this object, whose - // format is indicated by the object's addendum kind. - void *addendum_; - - void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true); - - AddendumKind addendumKind() const { - return (AddendumKind) - ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT); - } - - TypeNewScript *newScriptDontCheckGeneration() const { - if (addendumKind() == Addendum_NewScript) - return reinterpret_cast<TypeNewScript *>(addendum_); - return nullptr; - } - - UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const { - if (addendumKind() == Addendum_UnboxedLayout) - return reinterpret_cast<UnboxedLayout *>(addendum_); - return nullptr; - } - - TypeNewScript *anyNewScript(); - void detachNewScript(bool writeBarrier); - - public: - - ObjectGroupFlags flags() { - maybeSweep(nullptr); - return flags_; - } - - void addFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ |= flags; - } - - void clearFlags(ObjectGroupFlags flags) { - maybeSweep(nullptr); - flags_ &= ~flags; - } - - TypeNewScript *newScript() { - maybeSweep(nullptr); - return newScriptDontCheckGeneration(); - } - - void setNewScript(TypeNewScript *newScript) { - setAddendum(Addendum_NewScript, newScript); - } - - UnboxedLayout *maybeUnboxedLayout() { - maybeSweep(nullptr); - return maybeUnboxedLayoutDontCheckGeneration(); - } - - UnboxedLayout &unboxedLayout() { - MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); - return *maybeUnboxedLayout(); - } - - void setUnboxedLayout(UnboxedLayout *layout) { - setAddendum(Addendum_UnboxedLayout, layout); - } - - TypeDescr *maybeTypeDescr() { - // Note: there is no need to sweep when accessing the type descriptor - // of an object, as it is strongly held and immutable. - if (addendumKind() == Addendum_TypeDescr) - return reinterpret_cast<TypeDescr *>(addendum_); - return nullptr; - } - - TypeDescr &typeDescr() { - MOZ_ASSERT(addendumKind() == Addendum_TypeDescr); - return *maybeTypeDescr(); - } - - void setTypeDescr(TypeDescr *descr) { - setAddendum(Addendum_TypeDescr, descr); - } - - JSFunction *maybeInterpretedFunction() { - // Note: as with type descriptors, there is no need to sweep when - // accessing the interpreted function associated with an object. - if (addendumKind() == Addendum_InterpretedFunction) - return reinterpret_cast<JSFunction *>(addendum_); - return nullptr; - } - - void setInterpretedFunction(JSFunction *fun) { - setAddendum(Addendum_InterpretedFunction, fun); - } - - private: - /* - * Properties of this object. This may contain JSID_VOID, representing the - * types of all integer indexes of the object, and/or JSID_EMPTY, holding - * constraints listening to changes to the object's state. - * - * The type sets in the properties of a group describe the possible values - * that can be read out of that property in actual JS objects. In native - * objects, property types account for plain data properties (those with a - * slot and no getter or setter hook) and dense elements. In typed objects - * and unboxed objects, property types account for object and value - * properties and elements in the object. - * - * For accesses on these properties, the correspondence is as follows: - * - * 1. If the group has unknownProperties(), the possible properties and - * value types for associated JSObjects are unknown. - * - * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property - * in |obj|, before obj->getProperty(id) the property in |group| for - * |id| must reflect the result of the getProperty. - * - * There are several exceptions to this: - * - * 1. For properties of global JS objects which are undefined at the point - * where the property was (lazily) generated, the property type set will - * remain empty, and the 'undefined' type will only be added after a - * subsequent assignment or deletion. After these properties have been - * assigned a defined value, the only way they can become undefined - * again is after such an assign or deletion. - * - * 2. Array lengths are special cased by the compiler and VM and are not - * reflected in property types. - * - * 3. In typed objects (but not unboxed objects), the initial values of - * properties (null pointers and undefined values) are not reflected in - * the property types. These values are always possible when reading the - * property. - * - * We establish these by using write barriers on calls to setProperty and - * defineProperty which are on native properties, and on any jitcode which - * might update the property with a new type. - */ - Property **propertySet; - public: - - inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); - - bool hasAnyFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return !!(this->flags() & flags); - } - bool hasAllFlags(ObjectGroupFlags flags) { - MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); - return (this->flags() & flags) == flags; - } - - bool unknownProperties() { - MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, - hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); - return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); - } - - bool shouldPreTenure() { - return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); - } - - bool hasTenuredProto() { - return !(flags() & OBJECT_FLAG_NURSERY_PROTO); - } - - gc::InitialHeap initialHeap(CompilerConstraintList *constraints); - - bool canPreTenure() { - return !unknownProperties(); - } - - bool fromAllocationSite() { - return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; - } - - void setShouldPreTenure(ExclusiveContext *cx) { - MOZ_ASSERT(canPreTenure()); - setFlags(cx, OBJECT_FLAG_PRE_TENURE); - } - - /* - * Get or create a property of this object. Only call this for properties which - * a script accesses explicitly. - */ - inline HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); - - /* Get a property only if it already exists. */ - inline HeapTypeSet *maybeGetProperty(jsid id); - - inline unsigned getPropertyCount(); - inline Property *getProperty(unsigned i); - - /* Helpers */ - - void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types); - bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); - bool matchDefiniteProperties(HandleObject obj); - void markPropertyNonData(ExclusiveContext *cx, jsid id); - void markPropertyNonWritable(ExclusiveContext *cx, jsid id); - void markStateChange(ExclusiveContext *cx); - void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); - void markUnknown(ExclusiveContext *cx); - void maybeClearNewScriptOnOOM(); - void clearNewScript(ExclusiveContext *cx); - bool isPropertyNonData(jsid id); - bool isPropertyNonWritable(jsid id); - - void print(); - - inline void clearProperties(); - void maybeSweep(AutoClearTypeInferenceStateOnOOM *oom); - - private: -#ifdef DEBUG - bool needsSweep(); -#endif - - uint32_t generation() { - return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT; - } - - public: - void setGeneration(uint32_t generation) { - MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT)); - flags_ &= ~OBJECT_FLAG_GENERATION_MASK; - flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; - } - - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; - - inline void finalize(FreeOp *fop); - void fixupAfterMovingGC() {} - - static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } - - static inline uint32_t offsetOfClasp() { - return offsetof(ObjectGroup, clasp_); - } - - static inline uint32_t offsetOfProto() { - return offsetof(ObjectGroup, proto_); - } - - static inline uint32_t offsetOfAddendum() { - return offsetof(ObjectGroup, addendum_); - } - - static inline uint32_t offsetOfFlags() { - return offsetof(ObjectGroup, flags_); - } - - private: - inline uint32_t basePropertyCount(); - inline void setBasePropertyCount(uint32_t count); - - static void staticAsserts() { - JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto)); - } -}; +/* Is this a reasonable PC to be doing inlining on? */ +inline bool isInlinableCall(jsbytecode *pc); /* - * Entries for the per-compartment set of groups which are the default - * types to use for some prototype. An optional associated object is used which - * allows multiple groups to be created with the same prototype. The - * associated object may be a function (for types constructed with 'new') or a - * type descriptor (for typed objects). These entries are also used for the set - * of lazy groups in the compartment, which use a null associated object - * (though there are only a few of these per compartment). + * Type information about a property. + * + * The type sets in the properties of a group describe the possible values + * that can be read out of that property in actual JS objects. In native + * objects, property types account for plain data properties (those with a + * slot and no getter or setter hook) and dense elements. In typed objects + * and unboxed objects, property types account for object and value + * properties and elements in the object. + * + * For accesses on these properties, the correspondence is as follows: + * + * 1. If the group has unknownProperties(), the possible properties and + * value types for associated JSObjects are unknown. + * + * 2. Otherwise, for any |obj| in |group|, and any |id| which is a property + * in |obj|, before obj->getProperty(id) the property in |group| for + * |id| must reflect the result of the getProperty. + * + * There are several exceptions to this: + * + * 1. For properties of global JS objects which are undefined at the point + * where the property was (lazily) generated, the property type set will + * remain empty, and the 'undefined' type will only be added after a + * subsequent assignment or deletion. After these properties have been + * assigned a defined value, the only way they can become undefined + * again is after such an assign or deletion. + * + * 2. Array lengths are special cased by the compiler and VM and are not + * reflected in property types. + * + * 3. In typed objects (but not unboxed objects), the initial values of + * properties (null pointers and undefined values) are not reflected in + * the property types. These values are always possible when reading the + * property. + * + * We establish these by using write barriers on calls to setProperty and + * defineProperty which are on native properties, and on any jitcode which + * might update the property with a new type. */ -struct NewObjectGroupEntry +struct Property { - ReadBarrieredObjectGroup group; + /* Identifier for this property, JSID_VOID for the aggregate integer index property. */ + HeapId id; - // Note: This pointer is only used for equality and does not need a read barrier. - JSObject *associated; + /* Possible types for this property, including types inherited from prototypes. */ + HeapTypeSet types; - NewObjectGroupEntry(ObjectGroup *group, JSObject *associated) - : group(group), associated(associated) + explicit Property(jsid id) + : id(id) {} - struct Lookup { - const Class *clasp; - TaggedProto hashProto; - TaggedProto matchProto; - JSObject *associated; - - Lookup(const Class *clasp, TaggedProto proto, JSObject *associated) - : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) - {} + Property(const Property &o) + : id(o.id.get()), types(o.types) + {} - /* - * For use by generational post barriers only. Look up an entry whose - * proto has been moved, but was hashed with the original value. - */ - Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated) - : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) - {} - - }; - - static inline HashNumber hash(const Lookup &lookup); - static inline bool match(const NewObjectGroupEntry &key, const Lookup &lookup); - static void rekey(NewObjectGroupEntry &k, const NewObjectGroupEntry& newKey) { k = newKey; } + static uint32_t keyBits(jsid id) { return uint32_t(JSID_BITS(id)); } + static jsid getKey(Property *p) { return p->id; } }; -typedef HashSet<NewObjectGroupEntry, NewObjectGroupEntry, SystemAllocPolicy> NewObjectGroupTable; - -// Whether to make a singleton when calling 'new' at script/pc. -bool -UseSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc); - -// Whether to make a deep cloned singleton when cloning fun. -bool -UseSingletonForClone(JSFunction *fun); /* * Whether Array.prototype, or an object on its proto chain, has an * indexed property. */ bool ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints, JSScript *script); @@ -1421,20 +874,16 @@ class TypeScript /* Get the type set for values observed at an opcode. */ static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc); template <typename TYPESET> static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap, uint32_t *hint, TYPESET *typeArray); - /* Get a group for an allocation site in this script. */ - static inline ObjectGroup *InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, - JSProtoKey kind); - /* * Monitor a bytecode pushing any value. This must be called for any opcode * which is JOF_TYPESET, and where either the script has not been analyzed * by type inference or where the pc has type barriers. For simplicity, we * always monitor JOF_TYPESET opcodes in the interpreter and stub calls, * and only look at barriers when generating JIT code for the script. */ static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, @@ -1472,52 +921,30 @@ class TypeScript #ifdef DEBUG void printTypes(JSContext *cx, HandleScript script) const; #endif }; void FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap); -ArrayObject * -GetOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc); - -ArrayObject * -GetCopyOnWriteObject(JSScript *script, jsbytecode *pc); - class RecompileInfo; // Allocate a CompilerOutput for a finished compilation and generate the type // constraints for the compilation. Returns whether the type constraints // still hold. bool FinishCompilation(JSContext *cx, HandleScript script, CompilerConstraintList *constraints, RecompileInfo *precompileInfo); // Update the actual types in any scripts queried by constraints with any // speculative types added during the definite properties analysis. void FinishDefinitePropertiesAnalysis(JSContext *cx, CompilerConstraintList *constraints); -struct ArrayTableKey; -typedef HashMap<ArrayTableKey, - ReadBarrieredObjectGroup, - ArrayTableKey, - SystemAllocPolicy> ArrayTypeTable; - -struct ObjectTableKey; -struct ObjectTableEntry; -typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable; - -struct AllocationSiteKey; -typedef HashMap<AllocationSiteKey, - ReadBarrieredObjectGroup, - AllocationSiteKey, - SystemAllocPolicy> AllocationSiteTable; - class HeapTypeSetKey; // Type set entry for either a JSObject with singleton type or a non-singleton ObjectGroup. struct TypeSetObjectKey { static intptr_t keyBits(TypeSetObjectKey *obj) { return (intptr_t) obj; } static TypeSetObjectKey *getKey(TypeSetObjectKey *obj) { return obj; } @@ -1678,65 +1105,16 @@ class RecompileInfo CompilerOutput *compilerOutput(TypeZone &types) const; CompilerOutput *compilerOutput(JSContext *cx) const; bool shouldSweep(TypeZone &types); }; typedef Vector<RecompileInfo, 0, SystemAllocPolicy> RecompileInfoVector; -/* Type information for a compartment. */ -struct TypeCompartment -{ - /* Number of scripts in this compartment. */ - unsigned scriptCount; - - /* Table for referencing types of objects keyed to an allocation site. */ - AllocationSiteTable *allocationSiteTable; - - /* Tables for determining types of singleton/JSON objects. */ - ArrayTypeTable *arrayTypeTable; - ObjectTypeTable *objectTypeTable; - - private: - void setTypeToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type type); - - public: - void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); - void fixObjectGroup(ExclusiveContext *cx, PlainObject *obj); - void fixRestArgumentsType(ExclusiveContext *cx, ArrayObject *obj); - - JSObject *newTypedObject(JSContext *cx, IdValuePair *properties, size_t nproperties); - - TypeCompartment(); - ~TypeCompartment(); - - inline JSCompartment *compartment(); - - // Prints results of this compartment if spew is enabled or force is set. - void print(JSContext *cx, bool force); - - ObjectGroup *newObjectGroup(ExclusiveContext *cx, const Class *clasp, Handle<TaggedProto> proto, - ObjectGroupFlags initialFlags = 0); - - // Get or make a group for an allocation site, and add to the allocation site table. - ObjectGroup *addAllocationSiteObjectGroup(JSContext *cx, AllocationSiteKey key); - - void clearTables(); - void sweep(FreeOp *fop); - void finalizeObjects(); - - void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, - size_t *allocationSiteTables, - size_t *arrayTypeTables, - size_t *objectTypeTables); -}; - -void FixRestArgumentsType(ExclusiveContext *cxArg, ArrayObject *obj); - struct AutoEnterAnalysis; struct TypeZone { JS::Zone *zone_; /* Pool for type information in this zone. */ static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 8 * 1024; @@ -1814,20 +1192,24 @@ inline void InferSpew(SpewChannel which, inline const char * TypeString(Type type) { return nullptr; } inline const char * ObjectGroupString(ObjectGroup *group) { return nullptr; } #endif /* Print a warning, dump state and abort the program. */ MOZ_NORETURN MOZ_COLD void TypeFailure(JSContext *cx, const char *fmt, ...); +// Prints type information for a context if spew is enabled or force is set. +void +PrintTypes(JSContext *cx, JSCompartment *comp, bool force); + } /* namespace types */ } /* namespace js */ // JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances // with no associated compartment. namespace JS { namespace ubi { -template<> struct Concrete<js::types::ObjectGroup> : TracerConcrete<js::types::ObjectGroup> { }; +template<> struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> { }; } } #endif /* jsinfer_h */
--- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -318,97 +318,16 @@ struct AutoEnterAnalysis zone->types.activeAnalysis = this; } }; ///////////////////////////////////////////////////////////////////// // Interface functions ///////////////////////////////////////////////////////////////////// -inline const Class * -GetClassForProtoKey(JSProtoKey key) -{ - switch (key) { - case JSProto_Null: - case JSProto_Object: - return &PlainObject::class_; - case JSProto_Array: - return &ArrayObject::class_; - - case JSProto_Number: - return &NumberObject::class_; - case JSProto_Boolean: - return &BooleanObject::class_; - case JSProto_String: - return &StringObject::class_; - case JSProto_Symbol: - return &SymbolObject::class_; - case JSProto_RegExp: - return &RegExpObject::class_; - - case JSProto_Int8Array: - case JSProto_Uint8Array: - case JSProto_Int16Array: - case JSProto_Uint16Array: - case JSProto_Int32Array: - case JSProto_Uint32Array: - case JSProto_Float32Array: - case JSProto_Float64Array: - case JSProto_Uint8ClampedArray: - return &TypedArrayObject::classes[key - JSProto_Int8Array]; - - case JSProto_SharedInt8Array: - case JSProto_SharedUint8Array: - case JSProto_SharedInt16Array: - case JSProto_SharedUint16Array: - case JSProto_SharedInt32Array: - case JSProto_SharedUint32Array: - case JSProto_SharedFloat32Array: - case JSProto_SharedFloat64Array: - case JSProto_SharedUint8ClampedArray: - return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array]; - - case JSProto_ArrayBuffer: - return &ArrayBufferObject::class_; - - case JSProto_SharedArrayBuffer: - return &SharedArrayBufferObject::class_; - - case JSProto_DataView: - return &DataViewObject::class_; - - default: - MOZ_CRASH("Bad proto key"); - } -} - -/* - * Get the default 'new' group for a given standard class, per the currently - * active global. - */ -inline ObjectGroup * -GetNewObjectGroup(JSContext *cx, JSProtoKey key) -{ - RootedObject proto(cx); - if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto)) - return nullptr; - return cx->getNewGroup(GetClassForProtoKey(key), TaggedProto(proto.get())); -} - -/* Get a group for the immediate allocation site within a native. */ -inline ObjectGroup * -GetCallerInitGroup(JSContext *cx, JSProtoKey key) -{ - jsbytecode *pc; - RootedScript script(cx, cx->currentScript(&pc)); - if (script) - return TypeScript::InitGroup(cx, script, pc, key); - return GetNewObjectGroup(cx, key); -} - void MarkIteratorUnknownSlow(JSContext *cx); void TypeMonitorCallSlow(JSContext *cx, JSObject *callee, const CallArgs &args, bool constructing); /* * Monitor a javascript call, either on entry to the interpreter or made * from within the interpreter. @@ -564,33 +483,16 @@ IsTypePropertyIdMarkedNonWritable(JSObje /* Mark a state change on a particular object. */ inline void MarkObjectStateChange(ExclusiveContext *cx, JSObject *obj) { if (!obj->hasLazyGroup() && !obj->group()->unknownProperties()) obj->group()->markStateChange(cx); } -/* - * For an array or object which has not yet escaped and been referenced elsewhere, - * pick a new type based on the object's current contents. - */ - -inline void -FixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) -{ - cx->compartment()->types.fixArrayGroup(cx, obj); -} - -inline void -FixObjectGroup(ExclusiveContext *cx, PlainObject *obj) -{ - cx->compartment()->types.fixObjectGroup(cx, obj); -} - /* Interface helpers for JSScript*. */ extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval); extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type); ///////////////////////////////////////////////////////////////////// // Script interface functions @@ -674,95 +576,16 @@ TypeScript::BytecodeTypes(JSScript *scri TypeScript *types = script->types(); if (!types) return nullptr; uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets(); return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(), hint, types->typeArray()); } -struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> { - JSScript *script; - - uint32_t offset : 24; - JSProtoKey kind : 8; - - static const uint32_t OFFSET_LIMIT = (1 << 23); - - AllocationSiteKey() { mozilla::PodZero(this); } - - static inline uint32_t hash(AllocationSiteKey key) { - return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); - } - - static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { - return a.script == b.script && a.offset == b.offset && a.kind == b.kind; - } -}; - -/* Whether to use a singleton kind for an initializer opcode at script/pc. */ -js::NewObjectKind -UseSingletonForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key); - -js::NewObjectKind -UseSingletonForInitializer(JSScript *script, jsbytecode *pc, const Class *clasp); - -/* static */ inline ObjectGroup * -TypeScript::InitGroup(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind) -{ - MOZ_ASSERT(!UseSingletonForInitializer(script, pc, kind)); - - uint32_t offset = script->pcToOffset(pc); - - if (offset >= AllocationSiteKey::OFFSET_LIMIT) - return GetNewObjectGroup(cx, kind); - - AllocationSiteKey key; - key.script = script; - key.offset = offset; - key.kind = kind; - - if (!cx->compartment()->types.allocationSiteTable) - return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); - - AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(key); - - if (p) - return p->value(); - return cx->compartment()->types.addAllocationSiteObjectGroup(cx, key); -} - -/* Set the group to use for obj according to the site it was allocated at. */ -static inline bool -SetInitializerObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, HandleObject obj, - NewObjectKind kind) -{ - JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); - MOZ_ASSERT(key != JSProto_Null); - MOZ_ASSERT(kind == UseSingletonForInitializer(script, pc, key)); - - if (kind == SingletonObject) { - MOZ_ASSERT(obj->isSingleton()); - - /* - * Inference does not account for types of run-once initializer - * objects, as these may not be created until after the script - * has been analyzed. - */ - TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); - } else { - types::ObjectGroup *group = TypeScript::InitGroup(cx, script, pc, key); - if (!group) - return false; - obj->uninlinedSetGroup(group); - } - - return true; -} - /* static */ inline void TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval) { TypeMonitorResult(cx, script, pc, rval); } /* static */ inline void TypeScript::Monitor(JSContext *cx, const js::Value &rval) @@ -803,18 +626,18 @@ TypeScript::SetThis(JSContext *cx, JSScr { StackTypeSet *types = ThisTypes(script); if (!types) return; if (!types->hasType(type)) { AutoEnterAnalysis enter(cx); - InferSpew(ISpewOps, "externalType: setThis #%u: %s", - script->id(), TypeString(type)); + InferSpew(ISpewOps, "externalType: setThis %p: %s", + script, TypeString(type)); types->addType(cx, type); } } /* static */ inline void TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value) { SetThis(cx, script, GetValueType(value)); @@ -825,40 +648,30 @@ TypeScript::SetArgument(JSContext *cx, J { StackTypeSet *types = ArgTypes(script, arg); if (!types) return; if (!types->hasType(type)) { AutoEnterAnalysis enter(cx); - InferSpew(ISpewOps, "externalType: setArg #%u %u: %s", - script->id(), arg, TypeString(type)); + InferSpew(ISpewOps, "externalType: setArg %p %u: %s", + script, arg, TypeString(type)); types->addType(cx, type); } } /* static */ inline void TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value) { Type type = GetValueType(value); SetArgument(cx, script, arg, type); } ///////////////////////////////////////////////////////////////////// -// TypeCompartment -///////////////////////////////////////////////////////////////////// - -inline JSCompartment * -TypeCompartment::compartment() -{ - return (JSCompartment *)((char *)this - offsetof(JSCompartment, types)); -} - -///////////////////////////////////////////////////////////////////// // TypeSet ///////////////////////////////////////////////////////////////////// /* * The sets of objects and scripts in a type set grow monotonically, are usually * empty, almost always small, and sometimes big. For empty or singleton sets, * the pointer refers directly to the value. For sets fitting into SET_ARRAY_SIZE, * an array of this length is used to store the elements. For larger sets, a hash @@ -1184,76 +997,69 @@ TypeSet::getObjectClass(unsigned i) cons if (JSObject *object = getSingleton(i)) return object->getClass(); if (ObjectGroup *group = getGroup(i)) return group->clasp(); return nullptr; } ///////////////////////////////////////////////////////////////////// -// ObjectGroup +// TypeNewScript ///////////////////////////////////////////////////////////////////// -inline -ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags) +inline void +TypeNewScript::writeBarrierPre(TypeNewScript *newScript) { - mozilla::PodZero(this); - - /* Inner objects may not appear on prototype chains. */ - MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); + if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) + return; - this->clasp_ = clasp; - this->proto_ = proto.raw(); - this->flags_ = initialFlags; - - setGeneration(zone()->types.generation); - - InferSpew(ISpewOps, "newGroup: %s", ObjectGroupString(this)); + JS::Zone *zone = newScript->function()->zoneFromAnyThread(); + if (zone->needsIncrementalBarrier()) + newScript->trace(zone->barrierTracer()); } -inline void -ObjectGroup::finalize(FreeOp *fop) -{ - fop->delete_(newScriptDontCheckGeneration()); - fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); -} +} // namespace types + +///////////////////////////////////////////////////////////////////// +// ObjectGroup +///////////////////////////////////////////////////////////////////// inline uint32_t ObjectGroup::basePropertyCount() { return (flags() & OBJECT_FLAG_PROPERTY_COUNT_MASK) >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT; } inline void ObjectGroup::setBasePropertyCount(uint32_t count) { // Note: Callers must ensure they are performing threadsafe operations. MOZ_ASSERT(count <= OBJECT_FLAG_PROPERTY_COUNT_LIMIT); flags_ = (flags() & ~OBJECT_FLAG_PROPERTY_COUNT_MASK) | (count << OBJECT_FLAG_PROPERTY_COUNT_SHIFT); } -inline HeapTypeSet * +inline types::HeapTypeSet * ObjectGroup::getProperty(ExclusiveContext *cx, jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); - MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id)); MOZ_ASSERT(!unknownProperties()); - if (HeapTypeSet *types = maybeGetProperty(id)) + if (types::HeapTypeSet *types = maybeGetProperty(id)) return types; - Property *base = cx->typeLifoAlloc().new_<Property>(id); + types::Property *base = cx->typeLifoAlloc().new_<types::Property>(id); if (!base) { markUnknown(cx); return nullptr; } uint32_t propertyCount = basePropertyCount(); - Property **pprop = HashSetInsert<jsid,Property,Property> + types::Property **pprop = types::HashSetInsert<jsid,types::Property,types::Property> (cx->typeLifoAlloc(), propertySet, propertyCount, id); if (!pprop) { markUnknown(cx); return nullptr; } MOZ_ASSERT(!*pprop); @@ -1267,77 +1073,49 @@ ObjectGroup::getProperty(ExclusiveContex // the object unknown so that new properties will not be added in the // future. markUnknown(cx); } return &base->types; } -inline HeapTypeSet * +inline types::HeapTypeSet * ObjectGroup::maybeGetProperty(jsid id) { MOZ_ASSERT(JSID_IS_VOID(id) || JSID_IS_EMPTY(id) || JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); - MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == IdToTypeId(id)); + MOZ_ASSERT_IF(!JSID_IS_EMPTY(id), id == types::IdToTypeId(id)); MOZ_ASSERT(!unknownProperties()); - Property *prop = HashSetLookup<jsid,Property,Property> + types::Property *prop = types::HashSetLookup<jsid,types::Property,types::Property> (propertySet, basePropertyCount(), id); return prop ? &prop->types : nullptr; } inline unsigned ObjectGroup::getPropertyCount() { uint32_t count = basePropertyCount(); - if (count > SET_ARRAY_SIZE) - return HashSetCapacity(count); + if (count > types::SET_ARRAY_SIZE) + return types::HashSetCapacity(count); return count; } -inline Property * +inline types::Property * ObjectGroup::getProperty(unsigned i) { MOZ_ASSERT(i < getPropertyCount()); if (basePropertyCount() == 1) { MOZ_ASSERT(i == 0); - return (Property *) propertySet; + return (types::Property *) propertySet; } return propertySet[i]; } -inline void -TypeNewScript::writeBarrierPre(TypeNewScript *newScript) -{ - if (!newScript->function()->runtimeFromAnyThread()->needsIncrementalBarrier()) - return; - - JS::Zone *zone = newScript->function()->zoneFromAnyThread(); - if (zone->needsIncrementalBarrier()) - newScript->trace(zone->barrierTracer()); -} - -} } /* namespace js::types */ - -inline js::types::TypeScript * -JSScript::types() -{ - maybeSweepTypes(nullptr); - return types_; -} - -inline bool -JSScript::ensureHasTypes(JSContext *cx) -{ - return types() || makeTypes(cx); -} - -namespace js { - template <> struct GCMethods<const types::Type> { static types::Type initial() { return types::Type::UnknownType(); } static bool poisoned(const types::Type &v) { return (v.isGroup() && IsPoisonedPtr(v.group())) || (v.isSingleton() && IsPoisonedPtr(v.singleton())); } @@ -1350,9 +1128,22 @@ struct GCMethods<types::Type> static bool poisoned(const types::Type &v) { return (v.isGroup() && IsPoisonedPtr(v.group())) || (v.isSingleton() && IsPoisonedPtr(v.singleton())); } }; } // namespace js +inline js::types::TypeScript * +JSScript::types() +{ + maybeSweepTypes(nullptr); + return types_; +} + +inline bool +JSScript::ensureHasTypes(JSContext *cx) +{ + return types() || makeTypes(cx); +} + #endif /* jsinferinlines_h */
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -478,18 +478,18 @@ Compare(T *a, T *b, size_t c) } return true; } static inline PropertyIteratorObject * NewPropertyIteratorObject(JSContext *cx, unsigned flags) { if (flags & JSITER_ENUMERATE) { - RootedObjectGroup group(cx, cx->getNewGroup(&PropertyIteratorObject::class_, - TaggedProto(nullptr))); + 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_; @@ -575,17 +575,17 @@ RegisterEnumerator(JSContext *cx, Proper static inline bool VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, uint32_t slength, uint32_t key, MutableHandleObject objp) { MOZ_ASSERT(!(flags & JSITER_FOREACH)); if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) return false; NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys); if (!ni) return false; @@ -618,17 +618,17 @@ VectorToKeyIterator(JSContext *cx, Handl static bool VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, MutableHandleObject objp) { MOZ_ASSERT(flags & JSITER_FOREACH); if (obj->isSingleton() && !obj->setIteratedSingleton(cx)) return false; - types::MarkObjectGroupFlags(cx, obj, types::OBJECT_FLAG_ITERATED); + types::MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags)); if (!iterobj) return false; NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys); if (!ni) return false;
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1054,29 +1054,16 @@ js::DefineProperties(JSContext *cx, Hand for (size_t i = 0, len = ids.length(); i < len; i++) { if (!DefinePropertyOnObject(cx, obj.as<NativeObject>(), ids[i], descs[i], true, &dummy)) return false; } return true; } -js::types::ObjectGroup* -JSObject::uninlinedGetGroup(JSContext *cx) -{ - return getGroup(cx); -} - -void -JSObject::uninlinedSetGroup(js::types::ObjectGroup *group) -{ - setGroup(group); -} - - /*** Seal and freeze *****************************************************************************/ static unsigned GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level) { /* Make all attributes permanent; if freezing, make data attributes read-only. */ if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER))) return JSPROP_PERMANENT | JSPROP_READONLY; @@ -1263,17 +1250,17 @@ NewObjectGCKind(const js::Class *clasp) if (clasp == &ArrayObject::class_) return gc::FINALIZE_OBJECT8; if (clasp == &JSFunction::class_) return gc::FINALIZE_OBJECT2; return gc::FINALIZE_OBJECT4; } static inline JSObject * -NewObject(ExclusiveContext *cx, types::ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind, +NewObject(ExclusiveContext *cx, ObjectGroup *groupArg, JSObject *parent, gc::AllocKind kind, NewObjectKind newKind) { const Class *clasp = groupArg->clasp(); MOZ_ASSERT(clasp != &ArrayObject::class_); MOZ_ASSERT_IF(clasp == &JSFunction::class_, kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind); MOZ_ASSERT_IF(parent, &parent->global() == cx->global()); @@ -1362,17 +1349,17 @@ js::NewObjectWithGivenProto(ExclusiveCon gcNumber = rt->gc.gcNumber(); } } } Rooted<TaggedProto> proto(cxArg, protoArg); RootedObject parent(cxArg, parentArg); - types::ObjectGroup *group = cxArg->getNewGroup(clasp, proto, nullptr); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, proto, nullptr); if (!group) return nullptr; /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ if (!parent && proto.isObject()) @@ -1544,17 +1531,17 @@ js::NewObjectWithClassProtoCommon(Exclus RootedObject parent(cxArg, parentArg); RootedObject proto(cxArg, protoArg); if (!FindProto(cxArg, clasp, &proto)) return nullptr; Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto)); - types::ObjectGroup *group = cxArg->getNewGroup(clasp, taggedProto); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cxArg, clasp, taggedProto); if (!group) return nullptr; JSObject *obj = NewObject(cxArg, group, parent, allocKind, newKind); if (!obj) return nullptr; if (entry != -1 && !obj->as<NativeObject>().hasDynamicSlots() && @@ -1613,26 +1600,26 @@ js::NewObjectWithGroupCommon(JSContext * } bool js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj) { jsbytecode *pc; RootedScript script(cx, cx->currentScript(&pc)); gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); - NewObjectKind newKind = script - ? UseSingletonForInitializer(script, pc, &PlainObject::class_) - : GenericObject; + NewObjectKind newKind = GenericObject; + if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &PlainObject::class_)) + newKind = SingletonObject; RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind)); if (!obj) return false; if (script) { /* Try to specialize the group of the object to the scripted call site. */ - if (!types::SetInitializerObjectGroup(cx, script, pc, obj, newKind)) + if (!ObjectGroup::setAllocationSiteObjectGroup(cx, script, pc, obj, newKind == SingletonObject)) return false; } pobj.set(obj); return true; } JSObject* @@ -1702,30 +1689,30 @@ CreateThisForFunctionWithGroup(JSContext JSObject * js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto, NewObjectKind newKind /* = GenericObject */) { RootedObject res(cx); if (proto) { - RootedObjectGroup group(cx, cx->getNewGroup(nullptr, TaggedProto(proto), - &callee->as<JSFunction>())); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto), + &callee->as<JSFunction>())); if (!group) return nullptr; if (group->newScript() && !group->newScript()->analyzed()) { bool regenerate; if (!group->newScript()->maybeAnalyze(cx, group, ®enerate)) return nullptr; if (regenerate) { // The script was analyzed successfully and may have changed // the new type table, so refetch the group. - group = cx->getNewGroup(nullptr, TaggedProto(proto), - &callee->as<JSFunction>()); + group = ObjectGroup::defaultNewGroup(cx, nullptr, TaggedProto(proto), + &callee->as<JSFunction>()); MOZ_ASSERT(group && group->newScript()); } } res = CreateThisForFunctionWithGroup(cx, group, callee->getParent(), newKind); } else { gc::AllocKind allocKind = NewObjectGCKind(&PlainObject::class_); res = NewObjectWithProto<PlainObject>(cx, proto, callee->getParent(), allocKind, newKind); @@ -1975,19 +1962,19 @@ js::DeepCloneObjectLiteral(JSContext *cx } clone->setSlot(i, v); } if (obj->isSingleton()) { if (!JSObject::setSingleton(cx, clone)) return nullptr; } else if (obj->is<ArrayObject>()) { - FixArrayGroup(cx, &clone->as<ArrayObject>()); + ObjectGroup::fixArrayGroup(cx, &clone->as<ArrayObject>()); } else { - FixObjectGroup(cx, &clone->as<PlainObject>()); + ObjectGroup::fixPlainObjectGroup(cx, &clone->as<PlainObject>()); } if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) { if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone)) return nullptr; } return clone; @@ -2185,19 +2172,19 @@ js::XDRObjectLiteral(XDRState<mode> *xdr if (!xdr->codeUint32(&isSingletonTyped)) return false; if (mode == XDR_DECODE) { if (isSingletonTyped) { if (!JSObject::setSingleton(cx, obj)) return false; } else if (isArray) { - FixArrayGroup(cx, &obj->as<ArrayObject>()); + ObjectGroup::fixArrayGroup(cx, &obj->as<ArrayObject>()); } else { - FixObjectGroup(cx, &obj->as<PlainObject>()); + ObjectGroup::fixPlainObjectGroup(cx, &obj->as<PlainObject>()); } } { uint32_t frozen; bool extensible; if (mode == XDR_ENCODE) { if (!IsExtensible(cx, obj, &extensible)) @@ -2238,17 +2225,18 @@ js::CloneObjectLiteral(JSContext *cx, Ha { if (srcObj->is<PlainObject>()) { AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<PlainObject>().numFixedSlots())); MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind()); JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx); if (!proto) return nullptr; - RootedObjectGroup group(cx, cx->getNewGroup(&PlainObject::class_, TaggedProto(proto))); + RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, + TaggedProto(proto))); if (!group) return nullptr; RootedShape shape(cx, srcObj->lastProperty()); return NewReshapedObject(cx, group, parent, kind, shape); } RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>()); @@ -2743,17 +2731,17 @@ js::SetClassAndProto(JSContext *cx, Hand } if (proto.isObject()) { RootedObject protoObj(cx, proto.toObject()); if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj)) return false; } - ObjectGroup *group = cx->getNewGroup(clasp, proto); + ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, clasp, proto); if (!group) return false; /* * Setting __proto__ on an object that has escaped and may be referenced by * other heap objects can only be done if the properties of both objects * are unknown. Type sets containing this object will contain the original * type but not the new type of the object, so we need to treat all such
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -113,17 +113,17 @@ class JSObject : public js::gc::Cell friend class js::NewObjectCache; friend class js::Nursery; friend class js::gc::RelocationOverlay; friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded); friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); // Make a new group to use for a singleton object. - static js::types::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj); + static js::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj); public: js::Shape * lastProperty() const { MOZ_ASSERT(shape_); return shape_; } bool isNative() const { @@ -138,22 +138,22 @@ class JSObject : public js::gc::Cell } bool hasClass(const js::Class *c) const { return getClass() == c; } const js::ObjectOps *getOps() const { return &getClass()->ops; } - js::types::ObjectGroup *group() const { + js::ObjectGroup *group() const { MOZ_ASSERT(!hasLazyGroup()); return groupRaw(); } - js::types::ObjectGroup *groupRaw() const { + js::ObjectGroup *groupRaw() const { return group_; } /* * Whether this is the only object which has its specified gbroup. This * object will have its group constructed lazily as needed by analysis. */ bool isSingleton() const { @@ -318,19 +318,17 @@ class JSObject : public js::gc::Cell bool hasIdempotentProtoChain() const; /* * Marks this object as having a singleton type, and leave the group lazy. * Constructs a new, unique shape for the object. */ static inline bool setSingleton(js::ExclusiveContext *cx, js::HandleObject obj); - // uninlinedGetGroup() is the same as getGroup(), but not inlined. - inline js::types::ObjectGroup* getGroup(JSContext *cx); - js::types::ObjectGroup* uninlinedGetGroup(JSContext *cx); + inline js::ObjectGroup* getGroup(JSContext *cx); const js::HeapPtrObjectGroup &groupFromGC() const { /* Direct field access for use by GC. */ return group_; } /* * We allow the prototype of an object to be lazily computed if the object @@ -385,23 +383,17 @@ class JSObject : public js::gc::Cell // True iff this object's [[Prototype]] is immutable. Must not be called // on proxies with lazy [[Prototype]]! bool nonLazyPrototypeIsImmutable() const { MOZ_ASSERT(!hasLazyPrototype()); return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE); } - // uninlinedSetGroup() is the same as setGroup(), but not inlined. - inline void setGroup(js::types::ObjectGroup *group); - void uninlinedSetGroup(js::types::ObjectGroup *group); - -#ifdef DEBUG - bool hasNewGroup(const js::Class *clasp, js::types::ObjectGroup *group); -#endif + inline void setGroup(js::ObjectGroup *group); /* * Mark an object that has been iterated over and is a singleton. We need * to recover this information in the object's type information after it * is purged on GC. */ bool isIteratedSingleton() const { return lastProperty()->hasObjectFlag(js::BaseShape::ITERATED_SINGLETON);
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -70,40 +70,40 @@ JSObject::finalize(js::FreeOp *fop) shape_->listp = nullptr; } /* static */ inline bool JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj) { MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj)); - js::types::ObjectGroup *group = cx->getLazySingletonGroup(obj->getClass(), - obj->getTaggedProto()); + js::ObjectGroup *group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(), + obj->getTaggedProto()); if (!group) return false; obj->group_ = group; return true; } -inline js::types::ObjectGroup* +inline js::ObjectGroup* JSObject::getGroup(JSContext *cx) { MOZ_ASSERT(cx->compartment() == compartment()); if (hasLazyGroup()) { JS::RootedObject self(cx, this); if (cx->compartment() != compartment()) MOZ_CRASH(); return makeLazyGroup(cx, self); } return group_; } inline void -JSObject::setGroup(js::types::ObjectGroup *group) +JSObject::setGroup(js::ObjectGroup *group) { MOZ_ASSERT(group); MOZ_ASSERT(!isSingleton()); group_ = group; } /*** Standard internal methods *******************************************************************/
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -870,23 +870,16 @@ class JSScript : public js::gc::TenuredC uint32_t sourceEnd_; uint32_t warmUpCount; /* Number of times the script has been called * or has had backedges taken. When running in * ion, also increased for any inlined scripts. * Reset if the script's JIT code is forcibly * discarded. */ -#ifdef DEBUG - // Unique identifier within the compartment for this script, used for - // printing analysis information. - uint32_t id_; - uint32_t idpad; -#endif - // 16-bit fields. uint16_t version; /* JS version under which script was compiled */ uint16_t funLength_; /* ES6 function length */ uint16_t nTypeSets_; /* number of type sets used in this script for dynamic type monitoring */ @@ -1454,22 +1447,16 @@ class JSScript : public js::gc::TenuredC const char *filename() const { return scriptSource()->filename(); } const char *maybeForwardedFilename() const { return maybeForwardedScriptSource()->filename(); } public: /* Return whether this script was compiled for 'eval' */ bool isForEval() { return isCachedEval() || isActiveEval(); } -#ifdef DEBUG - unsigned id(); -#else - unsigned id() { return 0; } -#endif - /* Ensure the script has a TypeScript. */ inline bool ensureHasTypes(JSContext *cx); inline js::types::TypeScript *types(); void maybeSweepTypes(js::types::AutoClearTypeInferenceStateOnOOM *oom); inline js::GlobalObject &global() const;
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3780,17 +3780,17 @@ js::str_split(JSContext *cx, unsigned ar { CallArgs args = CallArgsFromVp(argc, vp); /* Steps 1-2. */ RootedString str(cx, ThisToStringForStringProto(cx, args)); if (!str) return false; - RootedObjectGroup group(cx, GetCallerInitGroup(cx, JSProto_Array)); + RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array)); if (!group) return false; AddTypePropertyId(cx, group, JSID_VOID, Type::StringType()); /* Step 5: Use the second argument as the split limit, if given. */ uint32_t limit; if (args.hasDefined(1)) { double d;
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -248,16 +248,17 @@ UNIFIED_SOURCES += [ 'vm/GlobalObject.cpp', 'vm/HelperThreads.cpp', 'vm/Id.cpp', 'vm/Interpreter.cpp', 'vm/JSONParser.cpp', 'vm/MemoryMetrics.cpp', 'vm/Monitor.cpp', 'vm/NativeObject.cpp', + 'vm/ObjectGroup.cpp', 'vm/PIC.cpp', 'vm/Probes.cpp', 'vm/ProxyObject.cpp', 'vm/RegExpObject.cpp', 'vm/RegExpStatics.cpp', 'vm/Runtime.cpp', 'vm/SavedStacks.cpp', 'vm/ScopeObject.cpp',
--- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -162,17 +162,17 @@ ArgumentsObject::create(JSContext *cx, H { RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx)); if (!proto) return nullptr; bool strict = callee->strict(); const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; - RootedObjectGroup group(cx, cx->getNewGroup(clasp, TaggedProto(proto.get()))); + 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),
--- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -518,17 +518,17 @@ ArrayBufferObject::neuter(JSContext *cx, // accesses such views needs to be deoptimized so that neuter checks are // performed. This is done by setting a compartment wide flag indicating // that buffers with typed object views have been neutered. if (buffer->hasTypedObjectViews()) { // Make sure the global object's group has been instantiated, so the // flag change will be observed. if (!cx->global()->getGroup(cx)) CrashAtUnhandlableOOM("ArrayBufferObject::neuter"); - types::MarkObjectGroupFlags(cx, cx->global(), types::OBJECT_FLAG_TYPED_OBJECT_NEUTERED); + types::MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_NEUTERED); cx->compartment()->neuteredTypedObjects = 1; } // Neuter all views on the buffer, clear out the list of views and the // buffer's data. if (InnerViewTable::ViewVector *views = cx->compartment()->innerViews.maybeViewsUnbarriered(buffer)) { for (size_t i = 0; i < views->length(); i++)
--- a/js/src/vm/ArrayObject-inl.h +++ b/js/src/vm/ArrayObject-inl.h @@ -17,17 +17,17 @@ namespace js { inline void ArrayObject::setLength(ExclusiveContext *cx, uint32_t length) { MOZ_ASSERT(lengthIsWritable()); if (length > INT32_MAX) { /* Track objects with overflowing lengths in type information. */ - types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW); } getElementsHeader()->length = length; } /* static */ inline ArrayObject * ArrayObject::createArrayInternal(ExclusiveContext *cx, gc::AllocKind kind, gc::InitialHeap heap, HandleShape shape, HandleObjectGroup group)
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -891,17 +891,17 @@ GlobalHelperThreadState::finishParseTask // Point the prototypes of any objects in the script's compartment to refer // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP); !iter.done(); iter.next()) { - types::ObjectGroup *group = iter.get<types::ObjectGroup>(); + ObjectGroup *group = iter.get<ObjectGroup>(); TaggedProto proto(group->proto()); if (!proto.isObject()) continue; JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject()); if (key == JSProto_Null) continue; MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -504,17 +504,17 @@ js::Invoke(JSContext *cx, CallArgs args, InvokeState state(cx, args, initial); // Check to see if createSingleton flag should be set for this frame. if (construct) { FrameIter iter(cx); if (!iter.done() && iter.hasScript()) { JSScript *script = iter.script(); jsbytecode *pc = iter.pc(); - if (UseSingletonForNewObject(cx, script, pc)) + if (ObjectGroup::useSingletonForNewObject(cx, script, pc)) state.setCreateSingleton(); } } bool ok = RunScript(cx, state); MOZ_ASSERT_IF(ok && construct, args.rval().isObject()); return ok; @@ -2559,17 +2559,17 @@ CASE(JSOP_FUNCALL) } Value *newsp = args.spAfterCall(); TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]); REGS.sp = newsp; ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH); } InitialFrameFlags initial = construct ? INITIAL_CONSTRUCT : INITIAL_NONE; - bool createSingleton = UseSingletonForNewObject(cx, script, REGS.pc); + bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, REGS.pc); TypeMonitorCall(cx, args, construct); { InvokeState state(cx, args, initial); if (createSingleton) state.setCreateSingleton(); @@ -3081,49 +3081,59 @@ CASE(JSOP_HOLE) END_CASE(JSOP_HOLE) CASE(JSOP_NEWINIT) { uint8_t i = GET_UINT8(REGS.pc); MOZ_ASSERT(i == JSProto_Array || i == JSProto_Object); RootedObject &obj = rootObject0; - NewObjectKind newKind; + NewObjectKind newKind = GenericObject; if (i == JSProto_Array) { - newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; obj = NewDenseEmptyArray(cx, nullptr, newKind); } else { gc::AllocKind allocKind = GuessObjectGCKind(0); - newKind = UseSingletonForInitializer(script, REGS.pc, &PlainObject::class_); + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &PlainObject::class_)) + newKind = SingletonObject; obj = NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind); } - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWINIT) CASE(JSOP_NEWARRAY) { unsigned count = GET_UINT24(REGS.pc); RootedObject &obj = rootObject0; - NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, &ArrayObject::class_); + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, &ArrayObject::class_)) + newKind = SingletonObject; obj = NewDenseFullyAllocatedArray(cx, count, nullptr, newKind); - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWARRAY) CASE(JSOP_NEWARRAY_COPYONWRITE) { RootedObject &baseobj = rootObject0; - baseobj = types::GetOrFixupCopyOnWriteObject(cx, script, REGS.pc); + baseobj = ObjectGroup::getOrFixupCopyOnWriteObject(cx, script, REGS.pc); if (!baseobj) goto error; RootedObject &obj = rootObject1; obj = NewDenseCopyOnWriteArray(cx, baseobj.as<ArrayObject>(), gc::DefaultHeap); if (!obj) goto error; @@ -3132,20 +3142,25 @@ CASE(JSOP_NEWARRAY_COPYONWRITE) END_CASE(JSOP_NEWARRAY_COPYONWRITE) CASE(JSOP_NEWOBJECT) { RootedObject &baseobj = rootObject0; baseobj = script->getObject(REGS.pc); RootedObject &obj = rootObject1; - NewObjectKind newKind = UseSingletonForInitializer(script, REGS.pc, baseobj->getClass()); + NewObjectKind newKind = GenericObject; + if (ObjectGroup::useSingletonForAllocationSite(script, REGS.pc, baseobj->getClass())) + newKind = SingletonObject; obj = CopyInitializerObject(cx, baseobj.as<PlainObject>(), newKind); - if (!obj || !SetInitializerObjectGroup(cx, script, REGS.pc, obj, newKind)) + if (!obj || !ObjectGroup::setAllocationSiteObjectGroup(cx, script, REGS.pc, obj, + newKind == SingletonObject)) + { goto error; + } PUSH_OBJECT(*obj); } END_CASE(JSOP_NEWOBJECT) CASE(JSOP_MUTATEPROTO) { MOZ_ASSERT(REGS.stackDepth() >= 2); @@ -3960,17 +3975,17 @@ js::RunOnceScriptPrologue(JSContext *cx, } // Force instantiation of the script's function's group to ensure the flag // is preserved in type information. if (!script->functionNonDelazifying()->getGroup(cx)) return false; types::MarkObjectGroupFlags(cx, script->functionNonDelazifying(), - types::OBJECT_FLAG_RUNONCE_INVALIDATED); + OBJECT_FLAG_RUNONCE_INVALIDATED); return true; } bool js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id, HandleObject val) { MOZ_ASSERT(val->isCallable());
--- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -576,22 +576,22 @@ JSONParser<CharT>::advanceAfterProperty( error("expected ',' or '}' after property value in object"); return token(Error); } JSObject * JSONParserBase::createFinishedObject(PropertyVector &properties) { /* - * Look for an existing cached type and shape for objects with this set of + * Look for an existing cached group and shape for objects with this set of * properties. */ { - JSObject *obj = cx->compartment()->types.newTypedObject(cx, properties.begin(), - properties.length()); + JSObject *obj = ObjectGroup::newPlainObject(cx, properties.begin(), + properties.length()); if (obj) return obj; } /* * Make a new object sized for the given number of properties and fill its * shape in manually. */ @@ -606,21 +606,21 @@ JSONParserBase::createFinishedObject(Pro for (size_t i = 0; i < properties.length(); i++) { propid = properties[i].id; value = properties[i].value; if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE)) return nullptr; } /* - * Try to assign a new type to the object with type information for its + * Try to assign a new group to the object with type information for its * properties, and update the initializer object group cache with this * object's final shape. */ - cx->compartment()->types.fixObjectGroup(cx, obj); + ObjectGroup::fixPlainObjectGroup(cx, obj); return obj; } inline bool JSONParserBase::finishObject(MutableHandleValue vp, PropertyVector &properties) { MOZ_ASSERT(&properties == &stack.back().properties()); @@ -640,18 +640,18 @@ inline bool JSONParserBase::finishArray(MutableHandleValue vp, ElementVector &elements) { MOZ_ASSERT(&elements == &stack.back().elements()); ArrayObject *obj = NewDenseCopiedArray(cx, elements.length(), elements.begin()); if (!obj) return false; - /* Try to assign a new type to the array according to its elements. */ - cx->compartment()->types.fixArrayGroup(cx, obj); + /* Try to assign a new group to the array according to its elements. */ + ObjectGroup::fixArrayGroup(cx, obj); vp.setObject(*obj); if (!freeElements.append(&elements)) return false; stack.popBack(); return true; }
--- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -545,17 +545,17 @@ StatsCellCallback(JSRuntime *rt, void *d const BaseShape *base = shape->base(); const Class *clasp = base->clasp(); const char *className = clasp->name; AddClassInfo(granularity, cStats, className, info); break; } case JSTRACE_OBJECT_GROUP: { - types::ObjectGroup *group = static_cast<types::ObjectGroup *>(thing); + ObjectGroup *group = static_cast<ObjectGroup *>(thing); zStats->objectGroupsGCHeap += thingSize; zStats->objectGroupsMallocHeap += group->sizeOfExcludingThis(rtStats->mallocSizeOf_); break; } default: MOZ_CRASH("invalid traceKind in StatsCellCallback"); }
--- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -94,42 +94,42 @@ NativeObject::initDenseElementWithType(E MOZ_ASSERT(!shouldConvertDoubleElements()); types::AddTypePropertyId(cx, this, JSID_VOID, val); initDenseElement(index, val); } inline void NativeObject::setDenseElementHole(ExclusiveContext *cx, uint32_t index) { - types::MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED); setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } /* static */ inline void NativeObject::removeDenseElementForSparseIndex(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index) { types::MarkObjectGroupFlags(cx, obj, - types::OBJECT_FLAG_NON_PACKED | - types::OBJECT_FLAG_SPARSE_INDEXES); + OBJECT_FLAG_NON_PACKED | + OBJECT_FLAG_SPARSE_INDEXES); if (obj->containsDenseElement(index)) obj->setDenseElement(index, MagicValue(JS_ELEMENTS_HOLE)); } inline bool NativeObject::writeToIndexWouldMarkNotPacked(uint32_t index) { return getElementsHeader()->initializedLength < index; } inline void NativeObject::markDenseElementsNotPacked(ExclusiveContext *cx) { MOZ_ASSERT(isNative()); - MarkObjectGroupFlags(cx, this, types::OBJECT_FLAG_NON_PACKED); + types::MarkObjectGroupFlags(cx, this, OBJECT_FLAG_NON_PACKED); } inline void NativeObject::ensureDenseInitializedLengthNoPackedCheck(ExclusiveContext *cx, uint32_t index, uint32_t extra) { MOZ_ASSERT(!denseElementsAreCopyOnWrite());
new file mode 100644 --- /dev/null +++ b/js/src/vm/ObjectGroup.cpp @@ -0,0 +1,1558 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/ObjectGroup.h" + +#include "jshashutil.h" +#include "jsobj.h" + +#include "gc/StoreBuffer.h" +#include "gc/Zone.h" +#include "vm/ArrayObject.h" +#include "vm/UnboxedObject.h" + +#include "jsgcinlines.h" +#include "jsinferinlines.h" +#include "jsobjinlines.h" + +using namespace js; +using namespace js::types; + +using mozilla::PodZero; + +///////////////////////////////////////////////////////////////////// +// ObjectGroup +///////////////////////////////////////////////////////////////////// + +ObjectGroup::ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags) +{ + PodZero(this); + + /* Inner objects may not appear on prototype chains. */ + MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); + + this->clasp_ = clasp; + this->proto_ = proto.raw(); + this->flags_ = initialFlags; + + setGeneration(zone()->types.generation); +} + +void +ObjectGroup::finalize(FreeOp *fop) +{ + fop->delete_(newScriptDontCheckGeneration()); + fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); +} + +void +ObjectGroup::setProto(JSContext *cx, TaggedProto proto) +{ + MOZ_ASSERT(singleton()); + + if (proto.isObject() && IsInsideNursery(proto.toObject())) + addFlags(OBJECT_FLAG_NURSERY_PROTO); + + setProtoUnchecked(proto); +} + +size_t +ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t n = 0; + if (TypeNewScript *newScript = newScriptDontCheckGeneration()) + n += newScript->sizeOfIncludingThis(mallocSizeOf); + if (UnboxedLayout *layout = maybeUnboxedLayoutDontCheckGeneration()) + n += layout->sizeOfIncludingThis(mallocSizeOf); + return n; +} + +void +ObjectGroup::setAddendum(AddendumKind kind, void *addendum, bool writeBarrier /* = true */) +{ + MOZ_ASSERT(!needsSweep()); + MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT)); + + if (writeBarrier) { + // Manually trigger barriers if we are clearing a TypeNewScript. Other + // kinds of addendums are immutable. + if (newScript()) + TypeNewScript::writeBarrierPre(newScript()); + else + MOZ_ASSERT(addendumKind() == Addendum_None || addendumKind() == kind); + } + + flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK; + flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT; + addendum_ = addendum; +} + +/* static */ bool +ObjectGroup::useSingletonForClone(JSFunction *fun) +{ + if (!fun->isInterpreted()) + return false; + + if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite()) + return true; + + if (fun->isArrow()) + return false; + + if (fun->isSingleton()) + return false; + + /* + * When a function is being used as a wrapper for another function, it + * improves precision greatly to distinguish between different instances of + * the wrapper; otherwise we will conflate much of the information about + * the wrapped functions. + * + * An important example is the Class.create function at the core of the + * Prototype.js library, which looks like: + * + * var Class = { + * create: function() { + * return function() { + * this.initialize.apply(this, arguments); + * } + * } + * }; + * + * Each instance of the innermost function will have a different wrapped + * initialize method. We capture this, along with similar cases, by looking + * for short scripts which use both .apply and arguments. For such scripts, + * whenever creating a new instance of the function we both give that + * instance a singleton type and clone the underlying script. + */ + + uint32_t begin, end; + if (fun->hasScript()) { + if (!fun->nonLazyScript()->usesArgumentsApplyAndThis()) + return false; + begin = fun->nonLazyScript()->sourceStart(); + end = fun->nonLazyScript()->sourceEnd(); + } else { + if (!fun->lazyScript()->usesArgumentsApplyAndThis()) + return false; + begin = fun->lazyScript()->begin(); + end = fun->lazyScript()->end(); + } + + return end - begin <= 100; +} + +/* static */ bool +ObjectGroup::useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + /* + * Make a heuristic guess at a use of JSOP_NEW that the constructed object + * should have a fresh group. We do this when the NEW is immediately + * followed by a simple assignment to an object's .prototype field. + * This is designed to catch common patterns for subclassing in JS: + * + * function Super() { ... } + * function Sub1() { ... } + * function Sub2() { ... } + * + * Sub1.prototype = new Super(); + * Sub2.prototype = new Super(); + * + * Using distinct groups for the particular prototypes of Sub1 and + * Sub2 lets us continue to distinguish the two subclasses and any extra + * properties added to those prototype objects. + */ + if (script->isGenerator()) + return false; + if (JSOp(*pc) != JSOP_NEW) + return false; + pc += JSOP_NEW_LENGTH; + if (JSOp(*pc) == JSOP_SETPROP) { + if (script->getName(pc) == cx->names().prototype) + return true; + } + return false; +} + +/* static */ bool +ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, JSProtoKey key) +{ + // The return value of this method can either be tested like a boolean or + // passed to a NewObject method. + JS_STATIC_ASSERT(GenericObject == 0); + + /* + * Objects created outside loops in global and eval scripts should have + * singleton types. For now this is only done for plain objects and typed + * arrays, but not normal arrays. + */ + + if (script->functionNonDelazifying() && !script->treatAsRunOnce()) + return GenericObject; + + if (key != JSProto_Object && + !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) && + !(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray)) + { + return GenericObject; + } + + /* + * All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note + * indicating their boundary. + */ + + if (!script->hasTrynotes()) + return SingletonObject; + + unsigned offset = script->pcToOffset(pc); + + JSTryNote *tn = script->trynotes()->vector; + JSTryNote *tnlimit = tn + script->trynotes()->length; + for (; tn < tnlimit; tn++) { + if (tn->kind != JSTRY_ITER && tn->kind != JSTRY_LOOP) + continue; + + unsigned startOffset = script->mainOffset() + tn->start; + unsigned endOffset = startOffset + tn->length; + + if (offset >= startOffset && offset < endOffset) + return GenericObject; + } + + return SingletonObject; +} + +/* static */ bool +ObjectGroup::useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, const Class *clasp) +{ + return useSingletonForAllocationSite(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp)); +} + +///////////////////////////////////////////////////////////////////// +// JSObject +///////////////////////////////////////////////////////////////////// + +bool +JSObject::shouldSplicePrototype(JSContext *cx) +{ + /* + * During bootstrapping, if inference is enabled we need to make sure not + * to splice a new prototype in for Function.prototype or the global + * object if their __proto__ had previously been set to null, as this + * will change the prototype for all other objects with the same type. + */ + if (getProto() != nullptr) + return false; + return isSingleton(); +} + +bool +JSObject::splicePrototype(JSContext *cx, const Class *clasp, Handle<TaggedProto> proto) +{ + MOZ_ASSERT(cx->compartment() == compartment()); + + RootedObject self(cx, this); + + /* + * For singleton groups representing only a single JSObject, the proto + * can be rearranged as needed without destroying type information for + * the old or new types. + */ + MOZ_ASSERT(self->isSingleton()); + + // Inner objects may not appear on prototype chains. + MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->getClass()->ext.outerObject); + + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return false; + + // Force type instantiation when splicing lazy group. + RootedObjectGroup group(cx, self->getGroup(cx)); + if (!group) + return false; + RootedObjectGroup protoGroup(cx, nullptr); + if (proto.isObject()) { + protoGroup = proto.toObject()->getGroup(cx); + if (!protoGroup) + return false; + } + + group->setClasp(clasp); + group->setProto(cx, proto); + return true; +} + +/* static */ ObjectGroup * +JSObject::makeLazyGroup(JSContext *cx, HandleObject obj) +{ + MOZ_ASSERT(obj->hasLazyGroup()); + MOZ_ASSERT(cx->compartment() == obj->compartment()); + + /* De-lazification of functions can GC, so we need to do it up here. */ + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) { + RootedFunction fun(cx, &obj->as<JSFunction>()); + if (!fun->getOrCreateScript(cx)) + return nullptr; + } + + // Find flags which need to be specified immediately on the object. + // Don't track whether singletons are packed. + ObjectGroupFlags initialFlags = OBJECT_FLAG_NON_PACKED; + + if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON)) + initialFlags |= OBJECT_FLAG_ITERATED; + + if (obj->isIndexed()) + initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; + + if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX) + initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; + + Rooted<TaggedProto> proto(cx, obj->getTaggedProto()); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto, + initialFlags); + if (!group) + return nullptr; + + AutoEnterAnalysis enter(cx); + + /* Fill in the type according to the state of this object. */ + + group->initSingleton(obj); + + if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) + group->setInterpretedFunction(&obj->as<JSFunction>()); + + obj->group_ = group; + + return group; +} + +/* static */ bool +JSObject::setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj) +{ + ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj); + return obj->setFlag(cx, BaseShape::NEW_GROUP_UNKNOWN); +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment NewTable +///////////////////////////////////////////////////////////////////// + +/* + * Entries for the per-compartment set of groups which are the default + * types to use for some prototype. An optional associated object is used which + * allows multiple groups to be created with the same prototype. The + * associated object may be a function (for types constructed with 'new') or a + * type descriptor (for typed objects). These entries are also used for the set + * of lazy groups in the compartment, which use a null associated object + * (though there are only a few of these per compartment). + */ +struct ObjectGroupCompartment::NewEntry +{ + ReadBarrieredObjectGroup group; + + // Note: This pointer is only used for equality and does not need a read barrier. + JSObject *associated; + + NewEntry(ObjectGroup *group, JSObject *associated) + : group(group), associated(associated) + {} + + struct Lookup { + const Class *clasp; + TaggedProto hashProto; + TaggedProto matchProto; + JSObject *associated; + + Lookup(const Class *clasp, TaggedProto proto, JSObject *associated) + : clasp(clasp), hashProto(proto), matchProto(proto), associated(associated) + {} + + /* + * For use by generational post barriers only. Look up an entry whose + * proto has been moved, but was hashed with the original value. + */ + Lookup(const Class *clasp, TaggedProto hashProto, TaggedProto matchProto, JSObject *associated) + : clasp(clasp), hashProto(hashProto), matchProto(matchProto), associated(associated) + {} + }; + + static inline HashNumber hash(const Lookup &lookup) { + return PointerHasher<JSObject *, 3>::hash(lookup.hashProto.raw()) ^ + PointerHasher<const Class *, 3>::hash(lookup.clasp) ^ + PointerHasher<JSObject *, 3>::hash(lookup.associated); + } + + static inline bool match(const NewEntry &key, const Lookup &lookup) { + return key.group->proto() == lookup.matchProto && + (!lookup.clasp || key.group->clasp() == lookup.clasp) && + key.associated == lookup.associated; + } + + static void rekey(NewEntry &k, const NewEntry& newKey) { k = newKey; } +}; + +// This class is used to add a post barrier on a NewTable entry, as the key is +// calculated from a prototype object which may be moved by generational GC. +class ObjectGroupCompartment::NewTableRef : public gc::BufferableRef +{ + NewTable *table; + const Class *clasp; + JSObject *proto; + JSObject *associated; + + public: + NewTableRef(NewTable *table, const Class *clasp, JSObject *proto, JSObject *associated) + : table(table), clasp(clasp), proto(proto), associated(associated) + {} + + void mark(JSTracer *trc) { + JSObject *prior = proto; + trc->setTracingLocation(&*prior); + Mark(trc, &proto, "newObjectGroups set prototype"); + if (prior == proto) + return; + + NewTable::Ptr p = table->lookup(NewTable::Lookup(clasp, TaggedProto(prior), + TaggedProto(proto), + associated)); + if (!p) + return; + + table->rekeyAs(NewTable::Lookup(clasp, TaggedProto(prior), TaggedProto(proto), associated), + NewTable::Lookup(clasp, TaggedProto(proto), associated), *p); + } +}; + +/* static */ void +ObjectGroupCompartment::newTablePostBarrier(ExclusiveContext *cx, NewTable *table, + const Class *clasp, TaggedProto proto, + JSObject *associated) +{ + MOZ_ASSERT_IF(associated, !IsInsideNursery(associated)); + + if (!proto.isObject()) + return; + + if (!cx->isJSContext()) { + MOZ_ASSERT(!IsInsideNursery(proto.toObject())); + return; + } + + if (IsInsideNursery(proto.toObject())) { + gc::StoreBuffer &sb = cx->asJSContext()->runtime()->gc.storeBuffer; + sb.putGeneric(NewTableRef(table, clasp, proto.toObject(), associated)); + } +} + +/* static */ ObjectGroup * +ObjectGroup::defaultNewGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto, JSObject *associated) +{ + MOZ_ASSERT_IF(associated, proto.isObject()); + MOZ_ASSERT_IF(associated, associated->is<JSFunction>() || associated->is<TypeDescr>()); + MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); + + // A null lookup clasp is used for 'new' groups with an associated + // function. The group starts out as a plain object but might mutate into an + // unboxed plain object. + MOZ_ASSERT(!clasp == (associated && associated->is<JSFunction>())); + + ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.defaultNewTable; + + if (!table) { + table = cx->new_<ObjectGroupCompartment::NewTable>(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + if (associated && associated->is<JSFunction>()) { + MOZ_ASSERT(!clasp); + + // Canonicalize new functions to use the original one associated with its script. + JSFunction *fun = &associated->as<JSFunction>(); + if (fun->hasScript()) + associated = fun->nonLazyScript()->functionNonDelazifying(); + else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin()) + associated = fun->lazyScript()->functionNonDelazifying(); + else + associated = nullptr; + + // If we have previously cleared the 'new' script information for this + // function, don't try to construct another one. + if (associated && associated->wasNewScriptCleared()) + associated = nullptr; + + if (!associated) + clasp = &PlainObject::class_; + } + + ObjectGroupCompartment::NewTable::AddPtr p = + table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated)); + if (p) { + ObjectGroup *group = p->group; + MOZ_ASSERT_IF(clasp, group->clasp() == clasp); + MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || + group->clasp() == &UnboxedPlainObject::class_); + MOZ_ASSERT(group->proto() == proto); + return group; + } + + AutoEnterAnalysis enter(cx); + + if (proto.isObject() && !proto.toObject()->setDelegate(cx)) + return nullptr; + + ObjectGroupFlags initialFlags = 0; + if (!proto.isObject() || proto.toObject()->isNewGroupUnknown()) + initialFlags = OBJECT_FLAG_DYNAMIC_MASK; + + Rooted<TaggedProto> protoRoot(cx, proto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_, + protoRoot, initialFlags); + if (!group) + return nullptr; + + if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) + return nullptr; + + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated); + + if (proto.isObject()) { + RootedObject obj(cx, proto.toObject()); + + if (associated) { + if (associated->is<JSFunction>()) + TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>()); + else + group->setTypeDescr(&associated->as<TypeDescr>()); + } + + /* + * Some builtin objects have slotful native properties baked in at + * creation via the Shape::{insert,get}initialShape mechanism. Since + * these properties are never explicitly defined on new objects, update + * the type information for them here. + */ + + const JSAtomState &names = cx->names(); + + if (obj->is<RegExpObject>()) { + AddTypePropertyId(cx, group, NameToId(names.source), Type::StringType()); + AddTypePropertyId(cx, group, NameToId(names.global), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.ignoreCase), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.multiline), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.sticky), Type::BooleanType()); + AddTypePropertyId(cx, group, NameToId(names.lastIndex), Type::Int32Type()); + } + + if (obj->is<StringObject>()) + AddTypePropertyId(cx, group, NameToId(names.length), Type::Int32Type()); + + if (obj->is<ErrorObject>()) { + AddTypePropertyId(cx, group, NameToId(names.fileName), Type::StringType()); + AddTypePropertyId(cx, group, NameToId(names.lineNumber), Type::Int32Type()); + AddTypePropertyId(cx, group, NameToId(names.columnNumber), Type::Int32Type()); + AddTypePropertyId(cx, group, NameToId(names.stack), Type::StringType()); + } + } + + return group; +} + +/* static */ ObjectGroup * +ObjectGroup::lazySingletonGroup(ExclusiveContext *cx, const Class *clasp, TaggedProto proto) +{ + MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment()); + + ObjectGroupCompartment::NewTable *&table = cx->compartment()->objectGroups.lazyTable; + + if (!table) { + table = cx->new_<ObjectGroupCompartment::NewTable>(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + ObjectGroupCompartment::NewTable::AddPtr p = + table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr)); + if (p) { + ObjectGroup *group = p->group; + MOZ_ASSERT(group->lazy()); + + return group; + } + + AutoEnterAnalysis enter(cx); + + Rooted<TaggedProto> protoRoot(cx, proto); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot); + if (!group) + return nullptr; + + if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) + return nullptr; + + ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, nullptr); + + group->initSingleton((JSObject *) ObjectGroup::LAZY_SINGLETON); + MOZ_ASSERT(group->singleton(), "created group must be a proper singleton"); + + return group; +} + +/* static */ void +ObjectGroup::setDefaultNewGroupUnknown(JSContext *cx, const Class *clasp, HandleObject obj) +{ + // If the object already has a new group, mark that group as unknown. + ObjectGroupCompartment::NewTable *table = cx->compartment()->objectGroups.defaultNewTable; + if (table) { + Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj)); + ObjectGroupCompartment::NewTable::Ptr p = + table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, taggedProto, nullptr)); + if (p) + MarkObjectGroupUnknownProperties(cx, p->group); + } +} + +#ifdef DEBUG +/* static */ bool +ObjectGroup::hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group) +{ + ObjectGroupCompartment::NewTable *table = proto->compartment()->objectGroups.defaultNewTable; + + if (table) { + ObjectGroupCompartment::NewTable::Ptr p = + table->lookup(ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr)); + return p && p->group == group; + } + return false; +} +#endif /* DEBUG */ + +inline const Class * +GetClassForProtoKey(JSProtoKey key) +{ + switch (key) { + case JSProto_Null: + case JSProto_Object: + return &PlainObject::class_; + case JSProto_Array: + return &ArrayObject::class_; + + case JSProto_Number: + return &NumberObject::class_; + case JSProto_Boolean: + return &BooleanObject::class_; + case JSProto_String: + return &StringObject::class_; + case JSProto_Symbol: + return &SymbolObject::class_; + case JSProto_RegExp: + return &RegExpObject::class_; + + case JSProto_Int8Array: + case JSProto_Uint8Array: + case JSProto_Int16Array: + case JSProto_Uint16Array: + case JSProto_Int32Array: + case JSProto_Uint32Array: + case JSProto_Float32Array: + case JSProto_Float64Array: + case JSProto_Uint8ClampedArray: + return &TypedArrayObject::classes[key - JSProto_Int8Array]; + + case JSProto_SharedInt8Array: + case JSProto_SharedUint8Array: + case JSProto_SharedInt16Array: + case JSProto_SharedUint16Array: + case JSProto_SharedInt32Array: + case JSProto_SharedUint32Array: + case JSProto_SharedFloat32Array: + case JSProto_SharedFloat64Array: + case JSProto_SharedUint8ClampedArray: + return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array]; + + case JSProto_ArrayBuffer: + return &ArrayBufferObject::class_; + + case JSProto_SharedArrayBuffer: + return &SharedArrayBufferObject::class_; + + case JSProto_DataView: + return &DataViewObject::class_; + + default: + MOZ_CRASH("Bad proto key"); + } +} + +/* static */ ObjectGroup * +ObjectGroup::defaultNewGroup(JSContext *cx, JSProtoKey key) +{ + RootedObject proto(cx); + if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto)) + return nullptr; + return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto.get())); +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment ArrayObjectTable +///////////////////////////////////////////////////////////////////// + +struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher<ArrayObjectKey> +{ + Type type; + JSObject *proto; + + ArrayObjectKey() + : type(Type::UndefinedType()), proto(nullptr) + {} + + ArrayObjectKey(Type type, JSObject *proto) + : type(type), proto(proto) + {} + + static inline uint32_t hash(const ArrayObjectKey &v) { + return (uint32_t) (v.type.raw() ^ ((uint32_t)(size_t)v.proto >> 2)); + } + + static inline bool match(const ArrayObjectKey &v1, const ArrayObjectKey &v2) { + return v1.type == v2.type && v1.proto == v2.proto; + } + + bool operator==(const ArrayObjectKey& other) { + return type == other.type && proto == other.proto; + } + + bool operator!=(const ArrayObjectKey& other) { + return !(*this == other); + } +}; + +static inline bool +NumberTypes(Type a, Type b) +{ + return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE)) + && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE)); +} + +/* + * As for GetValueType, but requires object types to be non-singletons with + * their default prototype. These are the only values that should appear in + * arrays and objects whose type can be fixed. + */ +static inline Type +GetValueTypeForTable(const Value &v) +{ + Type type = GetValueType(v); + MOZ_ASSERT(!type.isSingleton()); + return type; +} + +/* static */ void +ObjectGroup::fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj) +{ + AutoEnterAnalysis enter(cx); + + /* + * If the array is of homogenous type, pick a group which will be + * shared with all other singleton/JSON arrays of the same type. + * If the array is heterogenous, keep the existing group, which has + * unknown properties. + */ + + unsigned len = obj->getDenseInitializedLength(); + if (len == 0) + return; + + Type type = GetValueTypeForTable(obj->getDenseElement(0)); + + for (unsigned i = 1; i < len; i++) { + Type ntype = GetValueTypeForTable(obj->getDenseElement(i)); + if (ntype != type) { + if (NumberTypes(type, ntype)) + type = Type::DoubleType(); + else + return; + } + } + + setGroupToHomogenousArray(cx, obj, type); +} + +/* static */ void +ObjectGroup::fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj) +{ + AutoEnterAnalysis enter(cx); + + // Tracking element types for rest argument arrays is not worth it, but we + // still want it to be known that it's a dense array. + setGroupToHomogenousArray(cx, obj, Type::UnknownType()); +} + +/* static */ void +ObjectGroup::setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, Type elementType) +{ + MOZ_ASSERT(cx->zone()->types.activeAnalysis); + + ObjectGroupCompartment::ArrayObjectTable *&table = + cx->compartment()->objectGroups.arrayObjectTable; + + if (!table) { + table = cx->new_<ObjectGroupCompartment::ArrayObjectTable>(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return; + } + } + + ObjectGroupCompartment::ArrayObjectKey key(elementType, obj->getProto()); + DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key); + if (p) { + obj->setGroup(p->value()); + } else { + // Make a new group to use for future arrays with the same elements. + RootedObject objProto(cx, obj->getProto()); + Rooted<TaggedProto> taggedProto(cx, TaggedProto(objProto)); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto); + if (!group) + return; + obj->setGroup(group); + + AddTypePropertyId(cx, group, JSID_VOID, elementType); + + key.proto = objProto; + (void) p.add(cx, *table, key, group); + } +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment PlainObjectTable +///////////////////////////////////////////////////////////////////// + +/* + * N.B. We could also use the initial shape of the object (before its type is + * fixed) as the key in the object table, but since all references in the table + * are weak the hash entries would usually be collected on GC even if objects + * with the new type/shape are still live. + */ +struct ObjectGroupCompartment::PlainObjectKey +{ + jsid *properties; + uint32_t nproperties; + uint32_t nfixed; + + struct Lookup { + IdValuePair *properties; + uint32_t nproperties; + uint32_t nfixed; + + Lookup(IdValuePair *properties, uint32_t nproperties, uint32_t nfixed) + : properties(properties), nproperties(nproperties), nfixed(nfixed) + {} + }; + + static inline HashNumber hash(const Lookup &lookup) { + return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^ + lookup.nproperties ^ + lookup.nfixed); + } + + static inline bool match(const PlainObjectKey &v, const Lookup &lookup) { + if (lookup.nproperties != v.nproperties || lookup.nfixed != v.nfixed) + return false; + for (size_t i = 0; i < lookup.nproperties; i++) { + if (lookup.properties[i].id != v.properties[i]) + return false; + } + return true; + } +}; + +struct ObjectGroupCompartment::PlainObjectEntry +{ + ReadBarrieredObjectGroup group; + ReadBarrieredShape shape; + Type *types; +}; + +/* static */ void +ObjectGroupCompartment::updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry, + IdValuePair *properties, size_t nproperties) +{ + if (entry.group->unknownProperties()) + return; + for (size_t i = 0; i < nproperties; i++) { + Type type = entry.types[i]; + Type ntype = GetValueTypeForTable(properties[i].value); + if (ntype == type) + continue; + if (ntype.isPrimitive(JSVAL_TYPE_INT32) && + type.isPrimitive(JSVAL_TYPE_DOUBLE)) + { + /* The property types already reflect 'int32'. */ + } else { + if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) && + type.isPrimitive(JSVAL_TYPE_INT32)) + { + /* Include 'double' in the property types to avoid the update below later. */ + entry.types[i] = Type::DoubleType(); + } + AddTypePropertyId(cx, entry.group, IdToTypeId(properties[i].id), ntype); + } + } +} + +/* static */ void +ObjectGroup::fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj) +{ + AutoEnterAnalysis enter(cx); + + ObjectGroupCompartment::PlainObjectTable *&table = + cx->compartment()->objectGroups.plainObjectTable; + + if (!table) { + table = cx->new_<ObjectGroupCompartment::PlainObjectTable>(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return; + } + } + + /* + * Use the same group for all singleton/JSON objects with the same + * base shape, i.e. the same fields written in the same order. + * + * Exclude some objects we can't readily associate common types for based on their + * shape. Objects with metadata are excluded so that the metadata does not need to + * be included in the table lookup (the metadata object might be in the nursery). + */ + if (obj->slotSpan() == 0 || obj->inDictionaryMode() || !obj->hasEmptyElements() || obj->getMetadata()) + return; + + Vector<IdValuePair> properties(cx); + if (!properties.resize(obj->slotSpan())) + return; + + Shape *shape = obj->lastProperty(); + while (!shape->isEmptyShape()) { + IdValuePair &entry = properties[shape->slot()]; + entry.id = shape->propid(); + entry.value = obj->getSlot(shape->slot()); + shape = shape->previous(); + } + + ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties.begin(), properties.length(), + obj->numFixedSlots()); + ObjectGroupCompartment::PlainObjectTable::AddPtr p = table->lookupForAdd(lookup); + + if (p) { + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); + MOZ_ASSERT(obj->lastProperty() == p->value().shape); + + ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), + properties.begin(), + properties.length()); + obj->setGroup(p->value().group); + return; + } + + /* Make a new type to use for the object and similar future ones. */ + Rooted<TaggedProto> objProto(cx, obj->getTaggedProto()); + ObjectGroup *group = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, objProto); + if (!group || !group->addDefiniteProperties(cx, obj->lastProperty())) + return; + + if (obj->isIndexed()) + group->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES); + + ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(properties.length())); + if (!ids) + return; + + ScopedJSFreePtr<Type> types(group->zone()->pod_calloc<Type>(properties.length())); + if (!types) + return; + + for (size_t i = 0; i < properties.length(); i++) { + ids[i] = properties[i].id; + types[i] = GetValueTypeForTable(obj->getSlot(i)); + AddTypePropertyId(cx, group, IdToTypeId(ids[i]), types[i]); + } + + ObjectGroupCompartment::PlainObjectKey key; + key.properties = ids; + key.nproperties = properties.length(); + key.nfixed = obj->numFixedSlots(); + MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup)); + + ObjectGroupCompartment::PlainObjectEntry entry; + entry.group.set(group); + entry.shape.set(obj->lastProperty()); + entry.types = types; + + obj->setGroup(group); + + p = table->lookupForAdd(lookup); + if (table->add(p, key, entry)) { + ids.forget(); + types.forget(); + } +} + +/* static */ PlainObject * +ObjectGroup::newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties) +{ + AutoEnterAnalysis enter(cx); + + ObjectGroupCompartment::PlainObjectTable *table = + cx->compartment()->objectGroups.plainObjectTable; + + if (!table) + return nullptr; + + /* + * Use the object group table to allocate an object with the specified + * properties, filling in its final group and shape and failing if no table + * entry could be found for the properties. + */ + + /* + * Filter out a few cases where we don't want to use the object group table. + * Note that if the properties contain any duplicates or dense indexes, + * the lookup below will fail as such arrays of properties cannot be stored + * in the object group table --- fixObjectGroup populates the table with + * properties read off its input object, which cannot be duplicates, and + * ignores objects with dense indexes. + */ + if (!nproperties || nproperties >= PropertyTree::MAX_HEIGHT) + return nullptr; + + gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties); + size_t nfixed = gc::GetGCKindSlots(allocKind, &PlainObject::class_); + + ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties, nfixed); + ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookupForAdd(lookup); + + if (!p) + return nullptr; + + RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind)); + if (!obj) { + cx->clearPendingException(); + return nullptr; + } + MOZ_ASSERT(obj->getProto() == p->value().group->proto().toObject()); + + RootedShape shape(cx, p->value().shape); + if (!NativeObject::setLastProperty(cx, obj, shape)) { + cx->clearPendingException(); + return nullptr; + } + + ObjectGroupCompartment::updatePlainObjectEntryTypes(cx, p->value(), properties, nproperties); + + for (size_t i = 0; i < nproperties; i++) + obj->setSlot(i, properties[i].value); + + obj->setGroup(p->value().group); + return obj; +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment AllocationSiteTable +///////////////////////////////////////////////////////////////////// + +struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> { + JSScript *script; + + uint32_t offset : 24; + JSProtoKey kind : 8; + + static const uint32_t OFFSET_LIMIT = (1 << 23); + + AllocationSiteKey() { mozilla::PodZero(this); } + + static inline uint32_t hash(AllocationSiteKey key) { + return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind); + } + + static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) { + return a.script == b.script && a.offset == b.offset && a.kind == b.kind; + } +}; + +/* static */ ObjectGroup * +ObjectGroup::allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc, + JSProtoKey kind) +{ + MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind)); + + uint32_t offset = script->pcToOffset(pc); + + if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) + return defaultNewGroup(cx, kind); + + ObjectGroupCompartment::AllocationSiteKey key; + key.script = script; + key.offset = offset; + key.kind = kind; + + ObjectGroupCompartment::AllocationSiteTable *&table = + cx->compartment()->objectGroups.allocationSiteTable; + + if (!table) { + table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>(); + if (!table || !table->init()) { + js_delete(table); + table = nullptr; + return nullptr; + } + } + + ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key); + if (p) + return p->value(); + + AutoEnterAnalysis enter(cx); + + RootedObject proto(cx); + if (kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto)) + return nullptr; + + Rooted<TaggedProto> tagged(cx, TaggedProto(proto)); + ObjectGroup *res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged, + OBJECT_FLAG_FROM_ALLOCATION_SITE); + if (!res) + return nullptr; + + if (JSOp(*pc) == JSOP_NEWOBJECT) { + // This object is always constructed the same way and will not be + // observed by other code before all properties have been added. Mark + // all the properties as definite properties of the object. + JSObject *baseobj = script->getObject(GET_UINT32_INDEX(pc)); + if (!res->addDefiniteProperties(cx, baseobj->lastProperty())) + return nullptr; + } + + if (!table->add(p, key, res)) + return nullptr; + + return res; +} + +/* static */ ObjectGroup * +ObjectGroup::callingAllocationSiteGroup(JSContext *cx, JSProtoKey key) +{ + jsbytecode *pc; + RootedScript script(cx, cx->currentScript(&pc)); + if (script) + return allocationSiteGroup(cx, script, pc, key); + return defaultNewGroup(cx, key); +} + +/* static */ bool +ObjectGroup::setAllocationSiteObjectGroup(JSContext *cx, + HandleScript script, jsbytecode *pc, + HandleObject obj, bool singleton) +{ + JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass()); + MOZ_ASSERT(key != JSProto_Null); + MOZ_ASSERT(singleton == useSingletonForAllocationSite(script, pc, key)); + + if (singleton) { + MOZ_ASSERT(obj->isSingleton()); + + /* + * Inference does not account for types of run-once initializer + * objects, as these may not be created until after the script + * has been analyzed. + */ + TypeScript::Monitor(cx, script, pc, ObjectValue(*obj)); + } else { + ObjectGroup *group = allocationSiteGroup(cx, script, pc, key); + if (!group) + return false; + obj->setGroup(group); + } + + return true; +} + +/* static */ ArrayObject * +ObjectGroup::getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, jsbytecode *pc) +{ + // Make sure that the template object for script/pc has a type indicating + // that the object and its copies have copy on write elements. + RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>()); + MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); + + if (obj->group()->fromAllocationSite()) { + MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE)); + return obj; + } + + RootedObjectGroup group(cx, allocationSiteGroup(cx, script, pc, JSProto_Array)); + if (!group) + return nullptr; + + group->addFlags(OBJECT_FLAG_COPY_ON_WRITE); + + // Update type information in the initializer object group. + MOZ_ASSERT(obj->slotSpan() == 0); + for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { + const Value &v = obj->getDenseElement(i); + AddTypePropertyId(cx, group, JSID_VOID, v); + } + + obj->setGroup(group); + return obj; +} + +/* static */ ArrayObject * +ObjectGroup::getCopyOnWriteObject(JSScript *script, jsbytecode *pc) +{ + // getOrFixupCopyOnWriteObject should already have been called for + // script/pc, ensuring that the template object has a group with the + // COPY_ON_WRITE flag. We don't assert this here, due to a corner case + // where this property doesn't hold. See jsop_newarray_copyonwrite in + // IonBuilder. + ArrayObject *obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>(); + MOZ_ASSERT(obj->denseElementsAreCopyOnWrite()); + + return obj; +} + +/* static */ bool +ObjectGroup::findAllocationSiteForType(JSContext *cx, Type type, + JSScript **script, uint32_t *offset) +{ + *script = nullptr; + *offset = 0; + + if (type.isUnknown() || type.isAnyObject() || !type.isGroup()) + return false; + ObjectGroup *obj = type.group(); + + const ObjectGroupCompartment::AllocationSiteTable *table = + cx->compartment()->objectGroups.allocationSiteTable; + + if (!table) + return false; + + for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all(); + !r.empty(); + r.popFront()) + { + if (obj == r.front().value()) { + *script = r.front().key().script; + *offset = r.front().key().offset; + return true; + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////// +// ObjectGroupCompartment +///////////////////////////////////////////////////////////////////// + +ObjectGroupCompartment::ObjectGroupCompartment() +{ + PodZero(this); +} + +ObjectGroupCompartment::~ObjectGroupCompartment() +{ + js_delete(defaultNewTable); + js_delete(lazyTable); + js_delete(arrayObjectTable); + js_delete(plainObjectTable); + js_delete(allocationSiteTable); +} + +void +ObjectGroupCompartment::removeDefaultNewGroup(const Class *clasp, TaggedProto proto, + JSObject *associated) +{ + NewTable::Ptr p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated)); + MOZ_ASSERT(p); + + defaultNewTable->remove(p); +} + +void +ObjectGroupCompartment::replaceDefaultNewGroup(const Class *clasp, TaggedProto proto, + JSObject *associated, ObjectGroup *group) +{ + NewEntry::Lookup lookup(clasp, proto, associated); + + NewTable::Ptr p = defaultNewTable->lookup(lookup); + MOZ_ASSERT(p); + defaultNewTable->remove(p); + defaultNewTable->putNew(lookup, NewEntry(group, associated)); +} + +/* static */ +ObjectGroup * +ObjectGroupCompartment::makeGroup(ExclusiveContext *cx, const Class *clasp, + Handle<TaggedProto> proto, + ObjectGroupFlags initialFlags /* = 0 */) +{ + MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject())); + + if (cx->isJSContext()) { + if (proto.isObject() && IsInsideNursery(proto.toObject())) + initialFlags |= OBJECT_FLAG_NURSERY_PROTO; + } + + ObjectGroup *group = NewObjectGroup(cx); + if (!group) + return nullptr; + new(group) ObjectGroup(clasp, proto, initialFlags); + + return group; +} + +void +ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *allocationSiteTables, + size_t *arrayObjectGroupTables, + size_t *plainObjectGroupTables, + size_t *compartmentTables) +{ + if (allocationSiteTable) + *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf); + + if (arrayObjectTable) + *arrayObjectGroupTables += arrayObjectTable->sizeOfIncludingThis(mallocSizeOf); + + if (plainObjectTable) { + *plainObjectGroupTables += plainObjectTable->sizeOfIncludingThis(mallocSizeOf); + + for (PlainObjectTable::Enum e(*plainObjectTable); + !e.empty(); + e.popFront()) + { + const PlainObjectKey &key = e.front().key(); + const PlainObjectEntry &value = e.front().value(); + + /* key.ids and values.types have the same length. */ + *plainObjectGroupTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types); + } + } + + if (defaultNewTable) + *compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf); + + if (lazyTable) + *compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf); +} + +void +ObjectGroupCompartment::clearTables() +{ + if (allocationSiteTable && allocationSiteTable->initialized()) + allocationSiteTable->clear(); + if (arrayObjectTable && arrayObjectTable->initialized()) + arrayObjectTable->clear(); + if (plainObjectTable && plainObjectTable->initialized()) + plainObjectTable->clear(); + if (defaultNewTable && defaultNewTable->initialized()) + defaultNewTable->clear(); + if (lazyTable && lazyTable->initialized()) + lazyTable->clear(); +} + +void +ObjectGroupCompartment::sweep(FreeOp *fop) +{ + /* + * Iterate through the array/object group tables and remove all entries + * referencing collected data. These tables only hold weak references. + */ + + if (arrayObjectTable) { + for (ArrayObjectTable::Enum e(*arrayObjectTable); !e.empty(); e.popFront()) { + ArrayObjectKey key = e.front().key(); + MOZ_ASSERT(key.type.isUnknown() || !key.type.isSingleton()); + + bool remove = false; + if (!key.type.isUnknown() && key.type.isGroup()) { + ObjectGroup *group = key.type.groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group)) + remove = true; + else + key.type = Type::ObjectType(group); + } + if (key.proto && key.proto != TaggedProto::LazyProto && + IsObjectAboutToBeFinalizedFromAnyThread(&key.proto)) + { + remove = true; + } + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet())) + remove = true; + + if (remove) + e.removeFront(); + else if (key != e.front().key()) + e.rekeyFront(key); + } + } + + if (plainObjectTable) { + for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) { + const PlainObjectKey &key = e.front().key(); + PlainObjectEntry &entry = e.front().value(); + + bool remove = false; + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet())) + remove = true; + if (IsShapeAboutToBeFinalizedFromAnyThread(entry.shape.unsafeGet())) + remove = true; + for (unsigned i = 0; !remove && i < key.nproperties; i++) { + if (JSID_IS_STRING(key.properties[i])) { + JSString *str = JSID_TO_STRING(key.properties[i]); + if (IsStringAboutToBeFinalizedFromAnyThread(&str)) + remove = true; + MOZ_ASSERT(AtomToId((JSAtom *)str) == key.properties[i]); + } else if (JSID_IS_SYMBOL(key.properties[i])) { + JS::Symbol *sym = JSID_TO_SYMBOL(key.properties[i]); + if (IsSymbolAboutToBeFinalizedFromAnyThread(&sym)) + remove = true; + } + + MOZ_ASSERT(!entry.types[i].isSingleton()); + ObjectGroup *group = nullptr; + if (entry.types[i].isGroup()) { + group = entry.types[i].groupNoBarrier(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(&group)) + remove = true; + else if (group != entry.types[i].groupNoBarrier()) + entry.types[i] = Type::ObjectType(group); + } + } + + if (remove) { + js_free(key.properties); + js_free(entry.types); + e.removeFront(); + } + } + } + + if (allocationSiteTable) { + for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) { + AllocationSiteKey key = e.front().key(); + bool keyDying = IsScriptAboutToBeFinalizedFromAnyThread(&key.script); + bool valDying = IsObjectGroupAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()); + if (keyDying || valDying) + e.removeFront(); + else if (key.script != e.front().key().script) + e.rekeyFront(key); + } + } + + sweepNewTable(defaultNewTable); + sweepNewTable(lazyTable); +} + +void +ObjectGroupCompartment::sweepNewTable(NewTable *table) +{ + if (table && table->initialized()) { + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + if (IsObjectGroupAboutToBeFinalizedFromAnyThread(entry.group.unsafeGet()) || + (entry.associated && IsObjectAboutToBeFinalizedFromAnyThread(&entry.associated))) + { + e.removeFront(); + } else { + /* Any rekeying necessary is handled by fixupNewObjectGroupTable() below. */ + MOZ_ASSERT(entry.group.unbarrieredGet() == e.front().group.unbarrieredGet()); + MOZ_ASSERT(entry.associated == e.front().associated); + } + } + } +} + +void +ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable *table) +{ + /* + * Each entry's hash depends on the object's prototype and we can't tell + * whether that has been moved or not in sweepNewObjectGroupTable(). + */ + if (table && table->initialized()) { + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + bool needRekey = false; + if (IsForwarded(entry.group.get())) { + entry.group.set(Forwarded(entry.group.get())); + needRekey = true; + } + TaggedProto proto = entry.group->proto(); + if (proto.isObject() && IsForwarded(proto.toObject())) { + proto = TaggedProto(Forwarded(proto.toObject())); + needRekey = true; + } + if (entry.associated && IsForwarded(entry.associated)) { + entry.associated = Forwarded(entry.associated); + needRekey = true; + } + if (needRekey) { + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is<JSFunction>()) + clasp = nullptr; + NewEntry::Lookup lookup(clasp, proto, entry.associated); + e.rekeyFront(lookup, entry); + } + } + } +} + +#ifdef JSGC_HASH_TABLE_CHECKS + +void +ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable *table) +{ + /* + * Assert that nothing points into the nursery or needs to be relocated, and + * that the hash table entries are discoverable. + */ + if (!table || !table->initialized()) + return; + + for (NewTable::Enum e(*table); !e.empty(); e.popFront()) { + NewEntry entry = e.front(); + CheckGCThingAfterMovingGC(entry.group.get()); + TaggedProto proto = entry.group->proto(); + if (proto.isObject()) + CheckGCThingAfterMovingGC(proto.toObject()); + CheckGCThingAfterMovingGC(entry.associated); + + const Class *clasp = entry.group->clasp(); + if (entry.associated && entry.associated->is<JSFunction>()) + clasp = nullptr; + + NewEntry::Lookup lookup(clasp, proto, entry.associated); + NewTable::Ptr ptr = table->lookup(lookup); + MOZ_ASSERT(ptr.found() && &*ptr == &e.front()); + } +} + +#endif // JSGC_HASH_TABLE_CHECKS
new file mode 100644 --- /dev/null +++ b/js/src/vm/ObjectGroup.h @@ -0,0 +1,686 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_ObjectGroup_h +#define vm_ObjectGroup_h + +#include "jsbytecode.h" +#include "jsfriendapi.h" + +#include "ds/IdValuePair.h" +#include "gc/Barrier.h" + +namespace js { + +class TypeDescr; +class UnboxedLayout; + +namespace types { + +class Type; +class TypeNewScript; +class HeapTypeSet; +struct Property; +class AutoClearTypeInferenceStateOnOOM; +class CompilerConstraintList; + +} // namespace types + +// Information about an object prototype, which can be either a particular +// object, null, or a lazily generated object. The latter is only used by +// certain kinds of proxies. +class TaggedProto +{ + public: + static JSObject * const LazyProto; + + TaggedProto() : proto(nullptr) {} + explicit TaggedProto(JSObject *proto) : proto(proto) {} + + uintptr_t toWord() const { return uintptr_t(proto); } + + bool isLazy() const { + return proto == LazyProto; + } + bool isObject() const { + /* Skip nullptr and LazyProto. */ + return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); + } + JSObject *toObject() const { + MOZ_ASSERT(isObject()); + return proto; + } + JSObject *toObjectOrNull() const { + MOZ_ASSERT(!proto || isObject()); + return proto; + } + JSObject *raw() const { return proto; } + + bool operator ==(const TaggedProto &other) { return proto == other.proto; } + bool operator !=(const TaggedProto &other) { return proto != other.proto; } + + private: + JSObject *proto; +}; + +template <> +struct RootKind<TaggedProto> +{ + static ThingRootKind rootKind() { return THING_ROOT_OBJECT; } +}; + +template <> struct GCMethods<const TaggedProto> +{ + static TaggedProto initial() { return TaggedProto(); } + static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } +}; + +template <> struct GCMethods<TaggedProto> +{ + static TaggedProto initial() { return TaggedProto(); } + static bool poisoned(const TaggedProto &v) { return IsPoisonedPtr(v.raw()); } +}; + +template<class Outer> +class TaggedProtoOperations +{ + const TaggedProto *value() const { + return static_cast<const Outer*>(this)->extract(); + } + + public: + uintptr_t toWord() const { return value()->toWord(); } + inline bool isLazy() const { return value()->isLazy(); } + inline bool isObject() const { return value()->isObject(); } + inline JSObject *toObject() const { return value()->toObject(); } + inline JSObject *toObjectOrNull() const { return value()->toObjectOrNull(); } + JSObject *raw() const { return value()->raw(); } +}; + +template <> +class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto> > +{ + friend class TaggedProtoOperations<Handle<TaggedProto> >; + const TaggedProto * extract() const { + return static_cast<const Handle<TaggedProto>*>(this)->address(); + } +}; + +template <> +class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto> > +{ + friend class TaggedProtoOperations<Rooted<TaggedProto> >; + const TaggedProto *extract() const { + return static_cast<const Rooted<TaggedProto> *>(this)->address(); + } +}; + +/* Flags and other state stored in ObjectGroupFlags */ +enum : uint32_t { + /* Whether this group is associated with some allocation site. */ + OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1, + + /* + * If set, the object's prototype might be in the nursery and can't be + * used during Ion compilation (which may be occurring off thread). + */ + OBJECT_FLAG_NURSERY_PROTO = 0x2, + + /* Mask/shift for the number of properties in propertySet */ + OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8, + OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3, + OBJECT_FLAG_PROPERTY_COUNT_LIMIT = + OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT, + + /* Whether any objects this represents may have sparse indexes. */ + OBJECT_FLAG_SPARSE_INDEXES = 0x00010000, + + /* Whether any objects this represents may not have packed dense elements. */ + OBJECT_FLAG_NON_PACKED = 0x00020000, + + /* + * Whether any objects this represents may be arrays whose length does not + * fit in an int32. + */ + OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000, + + /* Whether any objects have been iterated over. */ + OBJECT_FLAG_ITERATED = 0x00080000, + + /* For a global object, whether flags were set on the RegExpStatics. */ + OBJECT_FLAG_REGEXP_FLAGS_SET = 0x00100000, + + /* + * For the function on a run-once script, whether the function has actually + * run multiple times. + */ + OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000, + + /* + * For a global object, whether any array buffers in this compartment with + * typed object views have been neutered. + */ + OBJECT_FLAG_TYPED_OBJECT_NEUTERED = 0x00400000, + + /* + * Whether objects with this type should be allocated directly in the + * tenured heap. + */ + OBJECT_FLAG_PRE_TENURE = 0x00800000, + + /* Whether objects with this type might have copy on write elements. */ + OBJECT_FLAG_COPY_ON_WRITE = 0x01000000, + + /* Whether this type has had its 'new' script cleared in the past. */ + OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000, + + /* + * Whether all properties of this object are considered unknown. + * If set, all other flags in DYNAMIC_MASK will also be set. + */ + OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000, + + /* Flags which indicate dynamic properties of represented objects. */ + OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000, + + // Mask/shift for the kind of addendum attached to this group. + OBJECT_FLAG_ADDENDUM_MASK = 0x38000000, + OBJECT_FLAG_ADDENDUM_SHIFT = 27, + + // Mask/shift for this group's generation. If out of sync with the + // TypeZone's generation, this group hasn't been swept yet. + OBJECT_FLAG_GENERATION_MASK = 0x40000000, + OBJECT_FLAG_GENERATION_SHIFT = 30, +}; +typedef uint32_t ObjectGroupFlags; + +/* + * Lazy object groups overview. + * + * Object groups which represent at most one JS object are constructed lazily. + * These include groups for native functions, standard classes, scripted + * functions defined at the top level of global/eval scripts, and in some + * other cases. Typical web workloads often create many windows (and many + * copies of standard natives) and many scripts, with comparatively few + * non-singleton groups. + * + * We can recover the type information for the object from examining it, + * so don't normally track the possible types of its properties as it is + * updated. Property type sets for the object are only constructed when an + * analyzed script attaches constraints to it: the script is querying that + * property off the object or another which delegates to it, and the analysis + * information is sensitive to changes in the property's type. Future changes + * to the property (whether those uncovered by analysis or those occurring + * in the VM) will treat these properties like those of any other object group. + */ + +/* Type information about an object accessed by a script. */ +class ObjectGroup : public gc::TenuredCell +{ + /* Class shared by objects in this group. */ + const Class *clasp_; + + /* Prototype shared by objects in this group. */ + HeapPtrObject proto_; + + /* + * Whether there is a singleton JS object with this group. That JS object + * must appear in type sets instead of this; we include the back reference + * here to allow reverting the JS object to a lazy group. + */ + HeapPtrObject singleton_; + + public: + + const Class *clasp() const { + return clasp_; + } + + void setClasp(const Class *clasp) { + clasp_ = clasp; + } + + TaggedProto proto() const { + return TaggedProto(proto_); + } + + JSObject *singleton() const { + return singleton_; + } + + // For use during marking, don't call otherwise. + HeapPtrObject &protoRaw() { return proto_; } + HeapPtrObject &singletonRaw() { return singleton_; } + + void setProto(JSContext *cx, TaggedProto proto); + void setProtoUnchecked(TaggedProto proto) { + proto_ = proto.raw(); + } + + void initSingleton(JSObject *singleton) { + singleton_ = singleton; + } + + /* + * Value held by singleton if this is a standin group for a singleton JS + * object whose group has not been constructed yet. + */ + static const size_t LAZY_SINGLETON = 1; + bool lazy() const { return singleton() == (JSObject *) LAZY_SINGLETON; } + + private: + /* Flags for this group. */ + ObjectGroupFlags flags_; + + // Kinds of addendums which can be attached to ObjectGroups. + enum AddendumKind { + Addendum_None, + + // When used by interpreted function, the addendum stores the + // canonical JSFunction object. + Addendum_InterpretedFunction, + + // When used by the 'new' group when constructing an interpreted + // function, the addendum stores a TypeNewScript. + Addendum_NewScript, + + // When objects in this group have an unboxed representation, the + // addendum stores an UnboxedLayout (which might have a TypeNewScript + // as well, if the group is also constructed using 'new'). + Addendum_UnboxedLayout, + + // When used by typed objects, the addendum stores a TypeDescr. + Addendum_TypeDescr + }; + + // If non-null, holds additional information about this object, whose + // format is indicated by the object's addendum kind. + void *addendum_; + + void setAddendum(AddendumKind kind, void *addendum, bool writeBarrier = true); + + AddendumKind addendumKind() const { + return (AddendumKind) + ((flags_ & OBJECT_FLAG_ADDENDUM_MASK) >> OBJECT_FLAG_ADDENDUM_SHIFT); + } + + types::TypeNewScript *newScriptDontCheckGeneration() const { + if (addendumKind() == Addendum_NewScript) + return reinterpret_cast<types::TypeNewScript *>(addendum_); + return nullptr; + } + + UnboxedLayout *maybeUnboxedLayoutDontCheckGeneration() const { + if (addendumKind() == Addendum_UnboxedLayout) + return reinterpret_cast<UnboxedLayout *>(addendum_); + return nullptr; + } + + types::TypeNewScript *anyNewScript(); + void detachNewScript(bool writeBarrier); + + public: + + ObjectGroupFlags flags() { + maybeSweep(nullptr); + return flags_; + } + + void addFlags(ObjectGroupFlags flags) { + maybeSweep(nullptr); + flags_ |= flags; + } + + void clearFlags(ObjectGroupFlags flags) { + maybeSweep(nullptr); + flags_ &= ~flags; + } + + types::TypeNewScript *newScript() { + maybeSweep(nullptr); + return newScriptDontCheckGeneration(); + } + + void setNewScript(types::TypeNewScript *newScript) { + setAddendum(Addendum_NewScript, newScript); + } + + UnboxedLayout *maybeUnboxedLayout() { + maybeSweep(nullptr); + return maybeUnboxedLayoutDontCheckGeneration(); + } + + UnboxedLayout &unboxedLayout() { + MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); + return *maybeUnboxedLayout(); + } + + void setUnboxedLayout(UnboxedLayout *layout) { + setAddendum(Addendum_UnboxedLayout, layout); + } + + TypeDescr *maybeTypeDescr() { + // Note: there is no need to sweep when accessing the type descriptor + // of an object, as it is strongly held and immutable. + if (addendumKind() == Addendum_TypeDescr) + return reinterpret_cast<TypeDescr *>(addendum_); + return nullptr; + } + + TypeDescr &typeDescr() { + MOZ_ASSERT(addendumKind() == Addendum_TypeDescr); + return *maybeTypeDescr(); + } + + void setTypeDescr(TypeDescr *descr) { + setAddendum(Addendum_TypeDescr, descr); + } + + JSFunction *maybeInterpretedFunction() { + // Note: as with type descriptors, there is no need to sweep when + // accessing the interpreted function associated with an object. + if (addendumKind() == Addendum_InterpretedFunction) + return reinterpret_cast<JSFunction *>(addendum_); + return nullptr; + } + + void setInterpretedFunction(JSFunction *fun) { + setAddendum(Addendum_InterpretedFunction, fun); + } + + private: + // Properties of this object. This may contain JSID_VOID, representing the + // types of all integer indexes of the object, and/or JSID_EMPTY, holding + // constraints listening to changes to the object's state. + // + // See types::Property for more detail about the types represented here. + types::Property **propertySet; + public: + + inline ObjectGroup(const Class *clasp, TaggedProto proto, ObjectGroupFlags initialFlags); + + inline bool hasAnyFlags(ObjectGroupFlags flags) { + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return !!(this->flags() & flags); + } + bool hasAllFlags(ObjectGroupFlags flags) { + MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags); + return (this->flags() & flags) == flags; + } + + bool unknownProperties() { + MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES, + hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK)); + return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES); + } + + bool shouldPreTenure() { + return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties(); + } + + bool hasTenuredProto() { + return !(flags() & OBJECT_FLAG_NURSERY_PROTO); + } + + gc::InitialHeap initialHeap(types::CompilerConstraintList *constraints); + + bool canPreTenure() { + return !unknownProperties(); + } + + bool fromAllocationSite() { + return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE; + } + + void setShouldPreTenure(ExclusiveContext *cx) { + MOZ_ASSERT(canPreTenure()); + setFlags(cx, OBJECT_FLAG_PRE_TENURE); + } + + /* + * Get or create a property of this object. Only call this for properties which + * a script accesses explicitly. + */ + inline types::HeapTypeSet *getProperty(ExclusiveContext *cx, jsid id); + + /* Get a property only if it already exists. */ + inline types::HeapTypeSet *maybeGetProperty(jsid id); + + inline unsigned getPropertyCount(); + inline types::Property *getProperty(unsigned i); + + /* Helpers */ + + void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, types::HeapTypeSet *types); + bool addDefiniteProperties(ExclusiveContext *cx, Shape *shape); + bool matchDefiniteProperties(HandleObject obj); + void markPropertyNonData(ExclusiveContext *cx, jsid id); + void markPropertyNonWritable(ExclusiveContext *cx, jsid id); + void markStateChange(ExclusiveContext *cx); + void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); + void markUnknown(ExclusiveContext *cx); + void maybeClearNewScriptOnOOM(); + void clearNewScript(ExclusiveContext *cx); + bool isPropertyNonData(jsid id); + bool isPropertyNonWritable(jsid id); + + void print(); + + inline void clearProperties(); + void maybeSweep(types::AutoClearTypeInferenceStateOnOOM *oom); + + private: +#ifdef DEBUG + bool needsSweep(); +#endif + + uint32_t generation() { + return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT; + } + + public: + void setGeneration(uint32_t generation) { + MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT)); + flags_ &= ~OBJECT_FLAG_GENERATION_MASK; + flags_ |= generation << OBJECT_FLAG_GENERATION_SHIFT; + } + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + void finalize(FreeOp *fop); + void fixupAfterMovingGC() {} + + static inline ThingRootKind rootKind() { return THING_ROOT_OBJECT_GROUP; } + + static inline uint32_t offsetOfClasp() { + return offsetof(ObjectGroup, clasp_); + } + + static inline uint32_t offsetOfProto() { + return offsetof(ObjectGroup, proto_); + } + + static inline uint32_t offsetOfAddendum() { + return offsetof(ObjectGroup, addendum_); + } + + static inline uint32_t offsetOfFlags() { + return offsetof(ObjectGroup, flags_); + } + + private: + inline uint32_t basePropertyCount(); + inline void setBasePropertyCount(uint32_t count); + + static void staticAsserts() { + JS_STATIC_ASSERT(offsetof(ObjectGroup, proto_) == offsetof(js::shadow::ObjectGroup, proto)); + } + + public: + // Whether to make a deep cloned singleton when cloning fun. + static bool useSingletonForClone(JSFunction *fun); + + // Whether to make a singleton when calling 'new' at script/pc. + static bool useSingletonForNewObject(JSContext *cx, JSScript *script, jsbytecode *pc); + + // Whether to make a singleton object at an allocation site. + static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, + JSProtoKey key); + static bool useSingletonForAllocationSite(JSScript *script, jsbytecode *pc, + const Class *clasp); + + // Static accessors for ObjectGroupCompartment NewTable. + + static ObjectGroup *defaultNewGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto, + JSObject *associated = nullptr); + static ObjectGroup *lazySingletonGroup(ExclusiveContext *cx, const Class *clasp, + TaggedProto proto); + + static void setDefaultNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj); + +#ifdef DEBUG + static bool hasDefaultNewGroup(JSObject *proto, const Class *clasp, ObjectGroup *group); +#endif + + // Static accessors for ObjectGroupCompartment ArrayObjectTable and PlainObjectTable. + + // Update the group of a freshly created array or plain object according to + // the object's current contents. + static void fixArrayGroup(ExclusiveContext *cx, ArrayObject *obj); + static void fixPlainObjectGroup(ExclusiveContext *cx, PlainObject *obj); + + // Update the group of a freshly created 'rest' arguments object. + static void fixRestArgumentsGroup(ExclusiveContext *cx, ArrayObject *obj); + + static PlainObject *newPlainObject(JSContext *cx, IdValuePair *properties, size_t nproperties); + + // Static accessors for ObjectGroupCompartment AllocationSiteTable. + + // Get a non-singleton group to use for objects created at the specified + // allocation site. + static ObjectGroup *allocationSiteGroup(JSContext *cx, JSScript *script, jsbytecode *pc, + JSProtoKey key); + + // Get a non-singleton group to use for objects created in a JSNative call. + static ObjectGroup *callingAllocationSiteGroup(JSContext *cx, JSProtoKey key); + + // Set the group or singleton-ness of an object created for an allocation site. + static bool + setAllocationSiteObjectGroup(JSContext *cx, HandleScript script, jsbytecode *pc, + HandleObject obj, bool singleton); + + static ArrayObject *getOrFixupCopyOnWriteObject(JSContext *cx, HandleScript script, + jsbytecode *pc); + static ArrayObject *getCopyOnWriteObject(JSScript *script, jsbytecode *pc); + + // Returns false if not found. + static bool findAllocationSiteForType(JSContext *cx, types::Type type, + JSScript **script, uint32_t *offset); + + private: + static ObjectGroup *defaultNewGroup(JSContext *cx, JSProtoKey key); + static void setGroupToHomogenousArray(ExclusiveContext *cx, JSObject *obj, types::Type type); +}; + +// Structure used to manage the groups in a compartment. +class ObjectGroupCompartment +{ + friend class ObjectGroup; + + struct NewEntry; + typedef HashSet<NewEntry, NewEntry, SystemAllocPolicy> NewTable; + class NewTableRef; + + // Set of default 'new' or lazy groups in the compartment. + NewTable *defaultNewTable; + NewTable *lazyTable; + + struct ArrayObjectKey; + typedef HashMap<ArrayObjectKey, + ReadBarrieredObjectGroup, + ArrayObjectKey, + SystemAllocPolicy> ArrayObjectTable; + + struct PlainObjectKey; + struct PlainObjectEntry; + typedef HashMap<PlainObjectKey, + PlainObjectEntry, + PlainObjectKey, + SystemAllocPolicy> PlainObjectTable; + + // Tables for managing groups common to the contents of large script + // singleton objects and JSON objects. These are vanilla ArrayObjects and + // PlainObjects, so we distinguish the groups of different ones by looking + // at the types of their properties. + // + // All singleton/JSON arrays which have the same prototype, are homogenous + // and of the same element type will share a group. All singleton/JSON + // objects which have the same shape and property types will also share a + // group. We don't try to collate arrays or objects with type mismatches. + ArrayObjectTable *arrayObjectTable; + PlainObjectTable *plainObjectTable; + + struct AllocationSiteKey; + typedef HashMap<AllocationSiteKey, + ReadBarrieredObjectGroup, + AllocationSiteKey, + SystemAllocPolicy> AllocationSiteTable; + + // Table for referencing types of objects keyed to an allocation site. + AllocationSiteTable *allocationSiteTable; + + public: + ObjectGroupCompartment(); + ~ObjectGroupCompartment(); + + void removeDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated); + void replaceDefaultNewGroup(const Class *clasp, TaggedProto proto, JSObject *associated, + ObjectGroup *group); + + static ObjectGroup *makeGroup(ExclusiveContext *cx, const Class *clasp, + Handle<TaggedProto> proto, + ObjectGroupFlags initialFlags = 0); + + void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, + size_t *allocationSiteTables, + size_t *arrayGroupTables, + size_t *plainObjectGroupTables, + size_t *compartmentTables); + + void clearTables(); + + void sweep(FreeOp *fop); + +#ifdef JSGC_HASH_TABLE_CHECKS + void checkTablesAfterMovingGC() { + checkNewTableAfterMovingGC(defaultNewTable); + checkNewTableAfterMovingGC(lazyTable); + } +#endif + + void fixupTablesAfterMovingGC() { + fixupNewTableAfterMovingGC(defaultNewTable); + fixupNewTableAfterMovingGC(lazyTable); + } + + private: +#ifdef JSGC_HASH_TABLE_CHECKS + void checkNewTableAfterMovingGC(NewTable *table); +#endif + + void sweepNewTable(NewTable *table); + void fixupNewTableAfterMovingGC(NewTable *table); + + static void newTablePostBarrier(ExclusiveContext *cx, NewTable *table, + const Class *clasp, TaggedProto proto, JSObject *associated); + static void updatePlainObjectEntryTypes(ExclusiveContext *cx, PlainObjectEntry &entry, + IdValuePair *properties, size_t nproperties); +}; + +} // namespace js + +#endif /* vm_ObjectGroup_h */
--- a/js/src/vm/ProxyObject.cpp +++ b/js/src/vm/ProxyObject.cpp @@ -61,17 +61,17 @@ ProxyObject::New(JSContext *cx, const Ba proxy->data.values = values; proxy->data.handler = handler; proxy->setCrossCompartmentPrivate(priv); /* Don't track types of properties of non-DOM and non-singleton proxies. */ if (newKind != SingletonObject && !clasp->isDOMClass()) - MarkObjectGroupUnknownProperties(cx, proxy->group()); + types::MarkObjectGroupUnknownProperties(cx, proxy->group()); return proxy; } void ProxyObject::setCrossCompartmentPrivate(const Value &priv) { *slotOfPrivate() = priv;