--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -422,25 +422,39 @@ void
js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
{
AssertRootMarkingPhase(trc);
DispatchToTracer(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
+js::TraceRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
+{
+ TraceRoot(trc, thingp->unsafeGet(), name);
+}
+
+template <typename T>
+void
js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name)
{
AssertRootMarkingPhase(trc);
if (InternalGCMethods<T>::isMarkableTaggedPointer(*thingp))
DispatchToTracer(trc, ConvertToBase(thingp), name);
}
template <typename T>
void
+js::TraceNullableRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
+{
+ TraceNullableRoot(trc, thingp->unsafeGet(), name);
+}
+
+template <typename T>
+void
js::TraceRange(JSTracer* trc, size_t len, WriteBarrieredBase<T>* vec, const char* name)
{
JS::AutoTracingIndex index(trc);
for (auto i : MakeRange(len)) {
if (InternalGCMethods<T>::isMarkable(vec[i].get()))
DispatchToTracer(trc, ConvertToBase(vec[i].unsafeUnbarrieredForTracing()), name);
++index;
}
@@ -460,17 +474,19 @@ js::TraceRootRange(JSTracer* trc, size_t
}
// Instantiate a copy of the Tracing templates for each derived type.
#define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
template void js::TraceEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
template void js::TraceWeakEdge<type>(JSTracer*, WeakRef<type>*, const char*); \
template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
+ template void js::TraceRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceNullableRoot<type>(JSTracer*, type*, const char*); \
+ template void js::TraceNullableRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
template void js::TraceRange<type>(JSTracer*, size_t, WriteBarrieredBase<type>*, const char*); \
template void js::TraceRootRange<type>(JSTracer*, size_t, type*, const char*);
FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
template <typename T>
void
js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -58,22 +58,30 @@ TraceEdge(JSTracer* trc, WriteBarrieredB
// Trace through a "root" edge. These edges are the initial edges in the object
// graph traversal. Root edges are asserted to only be traversed in the initial
// phase of a GC.
template <typename T>
void
TraceRoot(JSTracer* trc, T* thingp, const char* name);
+template <typename T>
+void
+TraceRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
+
// Idential to TraceRoot, except that this variant will not crash if |*thingp|
// is null.
template <typename T>
void
TraceNullableRoot(JSTracer* trc, T* thingp, const char* name);
+template <typename T>
+void
+TraceNullableRoot(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
+
// Like TraceEdge, but for edges that do not use one of the automatic barrier
// classes and, thus, must be treated specially for moving GC. This method is
// separate from TraceEdge to make accidental use of such edges more obvious.
template <typename T>
void
TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name);
// Visits a WeakRef, but does not trace its referents. If *thingp is not marked
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/arraySubclassPropertyLookup.js
@@ -0,0 +1,17 @@
+function f(v, expected) {
+ assertEq(v.prop, expected);
+};
+
+class SubArrayA extends Array {
+}
+class SubArrayB extends Array {
+}
+SubArrayA.prototype.prop = "A";
+SubArrayB.prototype.prop = "B";
+
+var a = new SubArrayA();
+var b = new SubArrayB();
+for (let i = 0; i < 10; i++) {
+ f(a, "A");
+ f(b, "B");
+}
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3055,19 +3055,19 @@ IsArrayConstructor(const Value& v)
// constructor would be represented as a wrapper.
return v.isObject() &&
v.toObject().is<JSFunction>() &&
v.toObject().as<JSFunction>().isNative() &&
v.toObject().as<JSFunction>().native() == ArrayConstructor;
}
static bool
-ArrayFromCallArgs(JSContext* cx, CallArgs& args)
+ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
{
- JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length());
+ JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
static bool
@@ -3178,18 +3178,22 @@ static const JSFunctionSpec array_static
};
/* ES5 15.4.2 */
bool
js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject proto(cx);
+ if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
+ return false;
+
if (args.length() != 1 || !args[0].isNumber())
- return ArrayFromCallArgs(cx, args);
+ return ArrayFromCallArgs(cx, args, proto);
uint32_t length;
if (args[0].isInt32()) {
int32_t i = args[0].toInt32();
if (i < 0) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
@@ -3198,17 +3202,17 @@ js::ArrayConstructor(JSContext* cx, unsi
double d = args[0].toDouble();
length = ToUint32(d);
if (d != double(length)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
}
- JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length);
+ JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
JSObject*
@@ -3305,38 +3309,37 @@ EnsureNewArrayElements(ExclusiveContext*
if (!obj->ensureElements(cx, length))
return false;
MOZ_ASSERT_IF(cap, !obj->hasDynamicElements());
return true;
}
-static bool
-NewArrayIsCachable(ExclusiveContext* cxArg, NewObjectKind newKind)
-{
- return cxArg->isJSContext() && newKind == GenericObject;
-}
-
template <uint32_t maxLength>
static MOZ_ALWAYS_INLINE ArrayObject*
NewArray(ExclusiveContext* cxArg, uint32_t length,
HandleObject protoArg, NewObjectKind newKind = GenericObject)
{
gc::AllocKind allocKind = GuessArrayGCKind(length);
MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
allocKind = GetBackgroundAllocKind(allocKind);
- bool isCachable = NewArrayIsCachable(cxArg, newKind);
+ RootedObject proto(cxArg, protoArg);
+ if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
+ return nullptr;
+
+ Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto));
+ bool isCachable = NewObjectWithTaggedProtoIsCachable(cxArg, taggedProto, newKind, &ArrayObject::class_);
if (isCachable) {
JSContext* cx = cxArg->asJSContext();
JSRuntime* rt = cx->runtime();
NewObjectCache& cache = rt->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
- if (cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry)) {
+ if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) {
gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
AutoSetNewObjectMetadata metadata(cx);
JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
if (obj) {
/* Fixup the elements pointer and length, which may be incorrect. */
ArrayObject* arr = &obj->as<ArrayObject>();
arr->setFixedElements();
arr->setLength(cx, length);
@@ -3345,20 +3348,16 @@ NewArray(ExclusiveContext* cxArg, uint32
{
return nullptr;
}
return arr;
}
}
}
- RootedObject proto(cxArg, protoArg);
- if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
- return nullptr;
-
RootedObjectGroup group(cxArg, ObjectGroup::defaultNewGroup(cxArg, &ArrayObject::class_,
TaggedProto(proto)));
if (!group)
return nullptr;
/*
* Get a shape with zero fixed slots, regardless of the size class.
* See JSObject::createArray.
@@ -3384,18 +3383,18 @@ NewArray(ExclusiveContext* cxArg, uint32
}
if (newKind == SingletonObject && !JSObject::setSingleton(cxArg, arr))
return nullptr;
if (isCachable) {
NewObjectCache& cache = cxArg->asJSContext()->runtime()->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
- cache.lookupGlobal(&ArrayObject::class_, cxArg->global(), allocKind, &entry);
- cache.fillGlobal(entry, &ArrayObject::class_, cxArg->global(), allocKind, arr);
+ cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry);
+ cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr);
}
if (maxLength > 0 && !EnsureNewArrayElements(cxArg, arr, std::min(maxLength, length)))
return nullptr;
probes::CreateObject(cxArg, arr);
return arr;
}
@@ -3485,38 +3484,41 @@ js::NewDenseCopyOnWriteArray(JSContext*
if (!arr)
return nullptr;
probes::CreateObject(cx, arr);
return arr;
}
// Return a new boxed or unboxed array with the specified length and allocated
-// capacity (up to maxLength), using the specified group if possible.
+// capacity (up to maxLength), using the specified group if possible. If the
+// specified group cannot be used, ensure that the created array at least has
+// the given [[Prototype]].
template <uint32_t maxLength>
static inline JSObject*
NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length,
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
{
MOZ_ASSERT(newKind != SingletonObject);
if (group->maybePreliminaryObjects())
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
newKind = TenuredObject;
+ RootedObject proto(cx, group->proto().toObject());
if (group->maybeUnboxedLayout()) {
- if (length > UnboxedArrayObject::MaximumCapacity)
- return NewArray<maxLength>(cx, length, nullptr, newKind);
-
+ if (length > UnboxedArrayObject::MaximumCapacity) {
+ return NewArray<maxLength>(cx, length, proto, newKind);
+ }
return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
}
- ArrayObject* res = NewArray<maxLength>(cx, length, nullptr, newKind);
+ ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
if (!res)
return nullptr;
res->setGroup(group);
// If the length calculation overflowed, make sure that is marked for the
// new group.
if (res->length() > INT32_MAX)
@@ -3585,19 +3587,19 @@ js::NewFullyAllocatedArrayForCallingAllo
{
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
if (!group)
return nullptr;
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind, forceAnalyze);
}
JSObject*
-js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length)
+js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
{
- RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
+ RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
if (!group)
return nullptr;
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
}
JSObject*
js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length, NewObjectKind newKind,
@@ -3647,19 +3649,20 @@ js::NewCopiedArrayTryUseGroup(ExclusiveC
MOZ_ASSERT(result != DenseElementResult::Incomplete);
if (result == DenseElementResult::Failure)
return nullptr;
return obj;
}
JSObject*
-js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length)
+js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
+ HandleObject proto /* = nullptr */)
{
- RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
+ RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
if (!group)
return nullptr;
return NewCopiedArrayTryUseGroup(cx, group, vp, length);
}
#ifdef DEBUG
bool
js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -96,32 +96,33 @@ extern JSObject*
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length);
extern JSObject*
NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
NewObjectKind newKind = GenericObject,
bool forceAnalyze = false);
extern JSObject*
-NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length);
+NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto);
enum class ShouldUpdateTypes
{
Update,
DontUpdate
};
extern JSObject*
NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group,
const Value* vp, size_t length,
NewObjectKind newKind = GenericObject,
ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update);
extern JSObject*
-NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length);
+NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
+ HandleObject proto = nullptr);
/*
* Determines whether a write to the given element on |obj| should fail because
* |obj| is an Array with a non-writable length, and writing that element would
* increase the length of the array.
*/
extern bool
WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -684,19 +684,19 @@ void
NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
gc::AllocKind kind, NativeObject* obj)
{
MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
MOZ_ASSERT(obj->getTaggedProto() == proto);
return fill(entry, clasp, proto.raw(), kind, obj);
}
-static bool
-NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
- NewObjectKind newKind, const Class* clasp)
+bool
+js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
+ NewObjectKind newKind, const Class* clasp)
{
return cxArg->isJSContext() &&
proto.isObject() &&
newKind == GenericObject &&
clasp->isNative() &&
!proto.toObject()->is<GlobalObject>();
}
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1084,16 +1084,20 @@ GetInitialHeap(NewObjectKind newKind, co
{
if (newKind != GenericObject)
return gc::TenuredHeap;
if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
return gc::TenuredHeap;
return gc::DefaultHeap;
}
+bool
+NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
+ NewObjectKind newKind, const Class* clasp);
+
// ES6 9.1.15 GetPrototypeFromConstructor.
extern bool
GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto);
extern bool
GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto);
// Specialized call for constructing |this| with a known function callee,
--- a/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js
+++ b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js
@@ -7,16 +7,17 @@ function testBuiltin(builtin, ...args) {
this.called = true;
}
}
let instance = new inst(...args);
assertEq(instance instanceof inst, true);
assertEq(instance instanceof builtin, true);
assertEq(instance.called, true);
+ return instance;
}
function testBuiltinTypedArrays() {
let typedArrays = [Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
@@ -28,16 +29,31 @@ function testBuiltinTypedArrays() {
for (let array of typedArrays) {
testBuiltin(array);
testBuiltin(array, 5);
testBuiltin(array, new array());
testBuiltin(array, new ArrayBuffer());
}
}
+function testBuiltinArray() {
+ let argsLists = [
+ [],
+ [15],
+ [3.0],
+ ["non-length one-arg"],
+ [5, 10, 15, "these are elements"]
+ ];
+
+ for (let args of argsLists) {
+ let instance = testBuiltin(Array, ...args);
+ assertEq(Array.isArray(instance), true);
+ }
+}
+
testBuiltin(Function);
testBuiltin(Object);
testBuiltin(Boolean);
testBuiltin(Error);
testBuiltin(EvalError);
testBuiltin(RangeError);
testBuiltin(ReferenceError);
testBuiltin(SyntaxError);
@@ -54,16 +70,17 @@ testBuiltin(Map);
testBuiltin(Set);
testBuiltin(WeakMap);
testBuiltin(WeakSet);
testBuiltin(ArrayBuffer);
testBuiltinTypedArrays();
testBuiltin(DataView, new ArrayBuffer());
testBuiltin(DataView, new (newGlobal().ArrayBuffer)());
testBuiltin(String);
+testBuiltinArray();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js
@@ -0,0 +1,29 @@
+var test = `
+
+class foo extends Array { }
+
+function testArrs(arrs) {
+ for (let arr of arrs) {
+ assertEq(Object.getPrototypeOf(arr), foo.prototype);
+ }
+}
+
+var arrs = [];
+for (var i = 0; i < 25; i++)
+ arrs.push(new foo(1));
+
+testArrs(arrs);
+
+arrs[0].nonIndexedProp = "uhoh";
+
+arrs.push(new foo(1));
+
+testArrs(arrs);
+
+`;
+
+if (classesEnabled())
+ eval(test);
+
+if (typeof reportCompare === 'function')
+ reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Reflect/construct.js
+++ b/js/src/tests/ecma_6/Reflect/construct.js
@@ -96,18 +96,16 @@ for (var ctor of constructors) {
for (var v of SOME_PRIMITIVE_VALUES.concat(nonConstructors)) {
assertThrowsInstanceOf(() => Reflect.construct(checkNewTarget, [], v), TypeError);
}
// The builtin Array constructor uses new.target.prototype and always
// creates a real array object.
function someConstructor() {}
var result = Reflect.construct(Array, [], someConstructor);
-assertEq(Reflect.getPrototypeOf(result),
- Array.prototype, // should be someConstructor.prototype, per ES6 22.1.1.1 Array()
- "Congratulations on implementing Array subclassing! Fix this test for +1 karma point.");
+assertEq(Reflect.getPrototypeOf(result), someConstructor.prototype);
assertEq(result.length, 0);
assertEq(Array.isArray(result), true);
// For more Reflect.construct tests, see target.js and argumentsList.js.
reportCompare(0, 0);
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -1352,74 +1352,93 @@ ObjectGroup::newPlainObject(ExclusiveCon
return obj;
}
/////////////////////////////////////////////////////////////////////
// ObjectGroupCompartment AllocationSiteTable
/////////////////////////////////////////////////////////////////////
-struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
- JSScript* script;
+struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey>,
+ public JS::Traceable {
+ ReadBarrieredScript script;
uint32_t offset : 24;
JSProtoKey kind : 8;
+ ReadBarrieredObject proto;
+
static const uint32_t OFFSET_LIMIT = (1 << 23);
- AllocationSiteKey() { mozilla::PodZero(this); }
+ AllocationSiteKey(JSScript* script_, uint32_t offset_, JSProtoKey kind_, JSObject* proto_)
+ : script(script_), offset(offset_), kind(kind_), proto(proto_)
+ {
+ MOZ_ASSERT(offset_ < OFFSET_LIMIT);
+ }
static inline uint32_t hash(AllocationSiteKey key) {
- return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind);
+ return uint32_t(size_t(key.script->offsetToPC(key.offset)) ^ key.kind ^
+ MovableCellHasher<JSObject*>::hash(key.proto));
}
static inline bool match(const AllocationSiteKey& a, const AllocationSiteKey& b) {
- return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
+ return DefaultHasher<JSScript*>::match(a.script, b.script) &&
+ a.offset == b.offset &&
+ a.kind == b.kind &&
+ MovableCellHasher<JSObject*>::match(a.proto, b.proto);
+ }
+
+ static void trace(AllocationSiteKey* key, JSTracer* trc) {
+ TraceRoot(trc, &key->script, "AllocationSiteKey script");
+ TraceNullableRoot(trc, &key->proto, "AllocationSiteKey proto");
}
};
/* static */ ObjectGroup*
-ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc,
- JSProtoKey kind)
+ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc,
+ JSProtoKey kind, HandleObject protoArg /* = nullptr */)
{
- MOZ_ASSERT(!useSingletonForAllocationSite(script, pc, kind));
-
- uint32_t offset = script->pcToOffset(pc);
+ MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
+ MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
- if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT)
- return defaultNewGroup(cx, kind);
+ uint32_t offset = scriptArg->pcToOffset(pc);
- ObjectGroupCompartment::AllocationSiteKey key;
- key.script = script;
- key.offset = offset;
- key.kind = kind;
+ if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) {
+ if (protoArg)
+ return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg));
+ return defaultNewGroup(cx, kind);
+ }
ObjectGroupCompartment::AllocationSiteTable*& table =
cx->compartment()->objectGroups.allocationSiteTable;
if (!table) {
table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>();
if (!table || !table->init()) {
ReportOutOfMemory(cx);
js_delete(table);
table = nullptr;
return nullptr;
}
}
+ RootedScript script(cx, scriptArg);
+ RootedObject proto(cx, protoArg);
+ if (!proto && kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto))
+ return nullptr;
+
+ Rooted<ObjectGroupCompartment::AllocationSiteKey> key(cx,
+ ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto));
+
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) {
// Keep track of the preliminary objects with this group, so we can try
@@ -1454,38 +1473,39 @@ ObjectGroup::allocationSiteGroup(JSConte
return res;
}
void
ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
JSProtoKey kind, ObjectGroup* group)
{
- AllocationSiteKey key;
- key.script = script;
- key.offset = script->pcToOffset(pc);
- key.kind = kind;
+ AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull());
AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
MOZ_RELEASE_ASSERT(p);
allocationSiteTable->remove(p);
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!allocationSiteTable->putNew(key, group))
oomUnsafe.crash("Inconsistent object table");
}
}
/* static */ ObjectGroup*
-ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key)
+ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, HandleObject proto)
{
+ MOZ_ASSERT_IF(proto, key == JSProto_Array);
+
jsbytecode* pc;
RootedScript script(cx, cx->currentScript(&pc));
if (script)
- return allocationSiteGroup(cx, script, pc, key);
+ return allocationSiteGroup(cx, script, pc, key, proto);
+ if (proto)
+ return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto));
return defaultNewGroup(cx, key);
}
/* static */ bool
ObjectGroup::setAllocationSiteObjectGroup(JSContext* cx,
HandleScript script, jsbytecode* pc,
HandleObject obj, bool singleton)
{
@@ -1760,23 +1780,21 @@ ObjectGroupCompartment::sweep(FreeOp* fo
js_free(entry.types);
e.removeFront();
}
}
}
if (allocationSiteTable) {
for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
- AllocationSiteKey key = e.front().key();
- bool keyDying = IsAboutToBeFinalizedUnbarriered(&key.script);
+ bool keyDying = IsAboutToBeFinalized(&e.front().mutableKey().script) ||
+ (e.front().key().proto && IsAboutToBeFinalized(&e.front().mutableKey().proto));
bool valDying = IsAboutToBeFinalized(&e.front().value());
if (keyDying || valDying)
e.removeFront();
- else if (key.script != e.front().key().script)
- e.rekeyFront(key);
}
}
sweepNewTable(defaultNewTable);
sweepNewTable(lazyTable);
}
void
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -505,20 +505,21 @@ class ObjectGroup : public gc::TenuredCe
IdValuePair* properties, size_t nproperties,
NewObjectKind newKind);
// 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);
+ JSProtoKey key, HandleObject proto = nullptr);
// Get a non-singleton group to use for objects created in a JSNative call.
- static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key);
+ static ObjectGroup* callingAllocationSiteGroup(JSContext* cx, JSProtoKey key,
+ HandleObject proto = nullptr);
// 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);