--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1628,16 +1628,18 @@ ReportTypedObjTypeError(JSContext *cx,
return false;
}
/* static */ void
OutlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
{
OutlineTypedObject &typedObj = object->as<OutlineTypedObject>();
+ MarkShape(trc, &typedObj.shape_, "OutlineTypedObject_shape");
+
if (!typedObj.owner_)
return;
// When this is called for compacting GC, the related objects we touch here
// may not have had their slots updated yet. Note that this does not apply
// to generational GC because these objects (type descriptors and
// prototypes) are never allocated in the nursery.
TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
@@ -2151,20 +2153,23 @@ InlineTypedObject::createCopy(JSContext
return res;
}
/* static */ void
InlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
{
InlineTypedObject &typedObj = object->as<InlineTypedObject>();
- // Inline transparent objects do not have references and do not need to be
- // traced. If they have an entry in the compartment's LazyArrayBufferTable,
+ MarkShape(trc, &typedObj.shape_, "InlineTypedObject_shape");
+
+ // Inline transparent objects do not have references and do not need more
+ // tracing. If there is an entry in the compartment's LazyArrayBufferTable,
// tracing that reference will be taken care of by the table itself.
- MOZ_ASSERT(typedObj.is<InlineOpaqueTypedObject>());
+ if (typedObj.is<InlineTransparentTypedObject>())
+ return;
// When this is called for compacting GC, the related objects we touch here
// may not have had their slots updated yet.
TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
descr.traceInstances(trc, typedObj.inlineTypedMem(), 1);
}
@@ -2332,17 +2337,17 @@ LazyArrayBufferTable::sizeOfIncludingThi
nullptr, /* getElements */ \
TypedObject::obj_enumerate, \
nullptr, /* thisObject */ \
} \
}
DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace);
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject, OutlineTypedObject::obj_trace);
-DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject, nullptr);
+DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject, InlineTypedObject::obj_trace);
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject, InlineTypedObject::obj_trace);
static int32_t
LengthForType(TypeDescr &descr)
{
switch (descr.kind()) {
case type::Scalar:
case type::Reference:
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -364,17 +364,17 @@ bool CreateUserSizeAndAlignmentPropertie
class ArrayTypeDescr;
/*
* Properties and methods of the `ArrayType` meta type object. There
* is no `class_` field because `ArrayType` is just a native
* constructor function.
*/
-class ArrayMetaTypeDescr : public JSObject
+class ArrayMetaTypeDescr : public NativeObject
{
private:
// Helper for creating a new ArrayType object.
//
// - `arrayTypePrototype` - prototype for the new object to be created
// - `elementType` - type object for the elements in the array
// - `stringRepr` - canonical string representation for the array
// - `size` - length of the array
@@ -428,17 +428,17 @@ class ArrayTypeDescr : public ComplexTyp
}
};
/*
* Properties and methods of the `StructType` meta type object. There
* is no `class_` field because `StructType` is just a native
* constructor function.
*/
-class StructMetaTypeDescr : public JSObject
+class StructMetaTypeDescr : public NativeObject
{
private:
static JSObject *create(JSContext *cx, HandleObject structTypeGlobal,
HandleObject fields);
public:
// Properties and methods to be installed on StructType.prototype,
// and hence inherited by all struct type objects:
@@ -505,26 +505,27 @@ class TypedObjectModuleObject : public N
};
static const Class class_;
};
/* Base type for transparent and opaque typed objects. */
class TypedObject : public JSObject
{
- private:
static const bool IsTypedObjectClass = true;
static bool obj_getArrayElement(JSContext *cx,
Handle<TypedObject*> typedObj,
Handle<TypeDescr*> typeDescr,
uint32_t index,
MutableHandleValue vp);
protected:
+ HeapPtrShape shape_;
+
static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
MutableHandleShape propp);
static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
MutableHandleObject objp, MutableHandleShape propp);
static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
@@ -596,16 +597,18 @@ class TypedObject : public JSObject
// User-accessible constructor (`new TypeDescriptor(...)`). Note that the
// callee here is the type descriptor.
static bool construct(JSContext *cx, unsigned argc, Value *vp);
/* Accessors for self hosted code. */
static bool GetBuffer(JSContext *cx, unsigned argc, Value *vp);
static bool GetByteOffset(JSContext *cx, unsigned argc, Value *vp);
+
+ Shape *shapeFromGC() { return shape_; }
};
typedef Handle<TypedObject*> HandleTypedObject;
class OutlineTypedObject : public TypedObject
{
// The object which owns the data this object points to. Because this
// pointer is managed in tandem with |data|, this is not a HeapPtr and
@@ -706,18 +709,16 @@ class InlineTypedObject : public TypedOb
static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
size_t nbytes = descr->size();
MOZ_ASSERT(nbytes <= MaximumSize);
return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
}
uint8_t *inlineTypedMem() const {
- static_assert(offsetof(InlineTypedObject, data_) == sizeof(JSObject),
- "The data for an inline typed object must follow the shape and type.");
return (uint8_t *) &data_;
}
static void obj_trace(JSTracer *trace, JSObject *object);
static void objectMovedDuringMinorGC(JSTracer *trc, JSObject *dst, JSObject *src);
static size_t offsetOfDataStart() {
return offsetof(InlineTypedObject, data_);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1673,51 +1673,54 @@ GCMarker::processMarkStackTop(SliceBudge
if (budget.isOverBudget()) {
repush(obj);
return;
}
ObjectGroup *group = obj->groupFromGC();
traverse(group);
- Shape *shape = obj->lastProperty();
- PushMarkStack(this, shape);
-
/* Call the trace hook if necessary. */
const Class *clasp = group->clasp();
if (clasp->trace) {
// Global objects all have the same trace hook. That hook is safe without barriers
// if the global has no custom trace hook of its own, or has been moved to a different
// compartment, and so can't have one.
MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
(!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
if (clasp->trace == InlineTypedObject::obj_trace) {
- TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
+ Shape *shape = obj->as<InlineTypedObject>().shapeFromGC();
+ PushMarkStack(this, shape);
+ TypeDescr *descr = &obj->as<InlineTypedObject>().typeDescr();
if (!descr->hasTraceList())
return;
unboxedTraceList = descr->traceList();
- unboxedMemory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
+ unboxedMemory = obj->as<InlineTypedObject>().inlineTypedMem();
goto scan_unboxed;
}
if (clasp == &UnboxedPlainObject::class_) {
const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
unboxedTraceList = layout.traceList();
if (!unboxedTraceList)
return;
unboxedMemory = obj->as<UnboxedPlainObject>().data();
goto scan_unboxed;
}
clasp->trace(this, obj);
}
- if (!shape->isNative())
+ if (!clasp->isNative())
return;
NativeObject *nobj = &obj->as<NativeObject>();
+
+ Shape *shape = nobj->lastProperty();
+ PushMarkStack(this, shape);
+
unsigned nslots = nobj->slotSpan();
do {
if (nobj->hasEmptyElements())
break;
if (nobj->denseElementsAreCopyOnWrite()) {
JSObject *owner = nobj->getElementsHeader()->ownerObject();
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -686,25 +686,26 @@ js::Nursery::moveObjectToTenured(MinorCo
if (src->is<ArrayObject>())
tenuredSize = srcSize = sizeof(NativeObject);
js_memcpy(dst, src, srcSize);
if (src->isNative()) {
NativeObject *ndst = &dst->as<NativeObject>(), *nsrc = &src->as<NativeObject>();
tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind);
tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind);
+
+ // The shape's list head may point into the old object. This can only
+ // happen for dictionaries, which are native objects.
+ if (&nsrc->shape_ == ndst->shape_->listp)
+ ndst->shape_->listp = &ndst->shape_;
}
if (src->is<InlineTypedObject>())
InlineTypedObject::objectMovedDuringMinorGC(trc, dst, src);
- /* The shape's list head may point into the old object. */
- if (&src->shape_ == dst->shape_->listp)
- dst->shape_->listp = &dst->shape_;
-
return tenuredSize;
}
MOZ_ALWAYS_INLINE size_t
js::Nursery::moveSlotsToTenured(NativeObject *dst, NativeObject *src, AllocKind dstKind)
{
/* Fixed slots have already been copied over. */
if (!src->hasDynamicSlots())
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3436,17 +3436,17 @@ IsCacheableGetPropCall(JSContext *cx, JS
static bool
IsCacheableSetPropWriteSlot(JSObject *obj, Shape *oldShape, JSObject *holder, Shape *shape)
{
if (!shape)
return false;
// Object shape must not have changed during the property set.
- if (obj->lastProperty() != oldShape)
+ if (!obj->isNative() || obj->as<NativeObject>().lastProperty() != oldShape)
return false;
// Currently we only optimize direct writes.
if (obj != holder)
return false;
if (!shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable())
return false;
@@ -3458,21 +3458,21 @@ static bool
IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
HandleId id, HandleObject holder, HandleShape shape,
size_t *protoChainDepth)
{
if (!shape)
return false;
// Property must be set directly on object, and be last added property of object.
- if (obj != holder || shape != obj->lastProperty())
+ if (!obj->isNative() || obj != holder || shape != obj->as<NativeObject>().lastProperty())
return false;
// Object must be extensible, oldShape must be immediate parent of curShape.
- if (!obj->nonProxyIsExtensible() || obj->lastProperty()->previous() != oldShape)
+ if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
return false;
// Basic shape checks.
if (shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter() ||
!shape->writable())
{
return false;
}
@@ -3557,17 +3557,17 @@ LookupNoSuchMethodHandler(JSContext *cx,
}
typedef bool (*LookupNoSuchMethodHandlerFn)(JSContext *, HandleObject, HandleValue,
MutableHandleValue);
static const VMFunction LookupNoSuchMethodHandlerInfo =
FunctionInfo<LookupNoSuchMethodHandlerFn>(LookupNoSuchMethodHandler);
static bool
-GetElemNativeStubExists(ICGetElem_Fallback *stub, HandleObject obj, HandleObject holder,
+GetElemNativeStubExists(ICGetElem_Fallback *stub, HandleNativeObject obj, HandleNativeObject holder,
HandlePropertyName propName, bool needsAtomize)
{
bool indirect = (obj.get() != holder.get());
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (iter->kind() != ICStub::GetElem_NativeSlot &&
iter->kind() != ICStub::GetElem_NativePrototypeSlot &&
iter->kind() != ICStub::GetElem_NativePrototypeCallNative &&
@@ -3621,18 +3621,18 @@ GetElemNativeStubExists(ICGetElem_Fallba
}
return true;
}
return false;
}
static void
-RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, HandleObject obj,
- HandleObject holder, HandlePropertyName propName,
+RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, HandleNativeObject obj,
+ HandleNativeObject holder, HandlePropertyName propName,
bool needsAtomize)
{
bool indirect = (obj.get() != holder.get());
for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
switch (iter->kind()) {
case ICStub::GetElem_NativeSlot:
if (indirect)
@@ -3699,17 +3699,17 @@ RemoveExistingGetElemNativeStubs(JSConte
}
static bool
TypedArrayGetElemStubExists(ICGetElem_Fallback *stub, HandleObject obj)
{
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (!iter->isGetElem_TypedArray())
continue;
- if (obj->lastProperty() == iter->toGetElem_TypedArray()->shape())
+ if (obj->maybeShape() == iter->toGetElem_TypedArray()->shape())
return true;
}
return false;
}
static bool
ArgumentsGetElemStubExists(ICGetElem_Fallback *stub, ICGetElem_Arguments::Which which)
{
@@ -3720,17 +3720,17 @@ ArgumentsGetElemStubExists(ICGetElem_Fal
return true;
}
return false;
}
static bool
TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc,
- ICGetElem_Fallback *stub, HandleObject obj,
+ ICGetElem_Fallback *stub, HandleNativeObject obj,
HandleValue key)
{
// Native-object GetElem stubs can't deal with non-string keys.
if (!key.isString())
return true;
// Convert to interned property name.
RootedId id(cx);
@@ -3741,19 +3741,23 @@ TryAttachNativeGetElemStub(JSContext *cx
if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
return true;
RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName());
bool needsAtomize = !key.toString()->isAtom();
bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
RootedShape shape(cx);
- RootedObject holder(cx);
- if (!EffectlesslyLookupProperty(cx, obj, propName, &holder, &shape))
- return false;
+ RootedObject baseHolder(cx);
+ if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape))
+ return false;
+ if (!baseHolder || !baseHolder->isNative())
+ return true;
+
+ HandleNativeObject holder = baseHolder.as<NativeObject>();
if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
// If a suitable stub already exists, nothing else to do.
if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
return true;
// Remove any existing stubs that may interfere with the new stub being added.
RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
@@ -3782,17 +3786,17 @@ TryAttachNativeGetElemStub(JSContext *cx
return false;
stub->addNewStub(newStub);
return true;
}
bool getterIsScripted = false;
bool isTemporarilyUnoptimizable = false;
- if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted,
+ if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted,
&isTemporarilyUnoptimizable, /*isDOMProxy=*/false)) {
RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
#if JS_HAS_NO_SUCH_METHOD
// It's unlikely that a getter function will be used in callelem locations.
// Just don't attach stubs in that case to avoid issues with __noSuchMethod__ handling.
if (isCallElem)
return true;
@@ -3944,29 +3948,29 @@ TryAttachGetElemStub(JSContext *cx, JSSc
}
}
if (obj->isNative()) {
// Check for NativeObject[int] dense accesses.
if (rhs.isInt32() && rhs.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) {
JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native[Int32] dense) stub");
ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
- obj->lastProperty(), isCallElem);
+ obj->as<NativeObject>().lastProperty(), isCallElem);
ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
if (!denseStub)
return false;
stub->addNewStub(denseStub);
return true;
}
// Check for NativeObject[id] shape-optimizable accesses.
if (rhs.isString()) {
RootedScript rootedScript(cx, script);
- if (!TryAttachNativeGetElemStub(cx, rootedScript, pc, stub, obj, rhs))
+ if (!TryAttachNativeGetElemStub(cx, rootedScript, pc, stub, obj.as<NativeObject>(), rhs))
return false;
script = rootedScript;
}
}
// Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) &&
rhs.isNumber() &&
@@ -3986,18 +3990,17 @@ TryAttachGetElemStub(JSContext *cx, JSSc
}
// Don't attach typed object stubs if they might be neutered, as the
// stub will always bail out.
if (IsPrimitiveArrayTypedObject(obj) && cx->compartment()->neuteredTypedObjects)
return true;
JitSpew(JitSpew_BaselineIC, " Generating GetElem(TypedArray[Int32]) stub");
- ICGetElem_TypedArray::Compiler compiler(cx, obj->lastProperty(),
- TypedThingElementType(obj));
+ ICGetElem_TypedArray::Compiler compiler(cx, obj->maybeShape(), TypedThingElementType(obj));
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
return false;
stub->addNewStub(typedArrayStub);
return true;
}
@@ -4882,33 +4885,39 @@ ICGetElem_Arguments::Compiler::generateS
return true;
}
//
// SetElem_Fallback
//
static bool
-SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd *stub, JSObject *obj)
+SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd *stub, NativeObject *obj)
{
size_t numShapes = stub->protoChainDepth() + 1;
for (size_t i = 0; i < numShapes; i++) {
static const size_t MAX_DEPTH = ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH;
if (obj->lastProperty() != stub->toImplUnchecked<MAX_DEPTH>()->shape(i))
return false;
- obj = obj->getProto();
- if (!obj && i != numShapes - 1)
- return false;
- }
-
- return true;
-}
-
-static bool
-DenseSetElemStubExists(JSContext *cx, ICStub::Kind kind, ICSetElem_Fallback *stub, HandleObject obj)
+ JSObject *proto = obj->getProto();
+ if (!proto) {
+ if (i != numShapes - 1)
+ return false;
+ break;
+ }
+ if (!proto->isNative())
+ return false;
+ obj = &proto->as<NativeObject>();
+ }
+
+ return true;
+}
+
+static bool
+DenseSetElemStubExists(JSContext *cx, ICStub::Kind kind, ICSetElem_Fallback *stub, HandleNativeObject obj)
{
MOZ_ASSERT(kind == ICStub::SetElem_Dense || kind == ICStub::SetElem_DenseAdd);
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) {
ICSetElem_Dense *dense = iter->toSetElem_Dense();
if (obj->lastProperty() == dense->shape() && obj->getGroup(cx) == dense->group())
return true;
@@ -4925,30 +4934,30 @@ DenseSetElemStubExists(JSContext *cx, IC
static bool
TypedArraySetElemStubExists(ICSetElem_Fallback *stub, HandleObject obj, bool expectOOB)
{
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
if (!iter->isSetElem_TypedArray())
continue;
ICSetElem_TypedArray *taStub = iter->toSetElem_TypedArray();
- if (obj->lastProperty() == taStub->shape() && taStub->expectOutOfBounds() == expectOOB)
+ if (obj->maybeShape() == taStub->shape() && taStub->expectOutOfBounds() == expectOOB)
return true;
}
return false;
}
static bool
RemoveExistingTypedArraySetElemStub(JSContext *cx, ICSetElem_Fallback *stub, HandleObject obj)
{
for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
if (!iter->isSetElem_TypedArray())
continue;
- if (obj->lastProperty() != iter->toSetElem_TypedArray()->shape())
+ if (obj->maybeShape() != iter->toSetElem_TypedArray()->shape())
continue;
// TypedArraySetElem stubs are only removed using this procedure if
// being replaced with one that expects out of bounds index.
MOZ_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
iter.unlink(cx);
return true;
}
@@ -5044,17 +5053,17 @@ DoSetElemFallback(JSContext *cx, Baselin
op == JSOP_INITELEM ||
op == JSOP_INITELEM_ARRAY ||
op == JSOP_INITELEM_INC);
RootedObject obj(cx, ToObjectFromStack(cx, objv));
if (!obj)
return false;
- RootedShape oldShape(cx, obj->lastProperty());
+ RootedShape oldShape(cx, obj->maybeShape());
// Check the old capacity
uint32_t oldCapacity = 0;
uint32_t oldInitLength = 0;
if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
oldCapacity = obj->as<NativeObject>().getDenseCapacity();
oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
}
@@ -5089,47 +5098,49 @@ DoSetElemFallback(JSContext *cx, Baselin
}
// Try to generate new stubs.
if (obj->isNative() &&
!IsAnyTypedArray(obj.get()) &&
index.isInt32() && index.toInt32() >= 0 &&
!rhs.isMagic(JS_ELEMENTS_HOLE))
{
+ HandleNativeObject nobj = obj.as<NativeObject>();
+
bool addingCase;
size_t protoDepth;
- if (CanOptimizeDenseSetElem(&obj->as<NativeObject>(), index.toInt32(),
+ if (CanOptimizeDenseSetElem(nobj, index.toInt32(),
oldShape, oldCapacity, oldInitLength,
&addingCase, &protoDepth))
{
- RootedShape shape(cx, obj->lastProperty());
+ RootedShape shape(cx, nobj->lastProperty());
RootedObjectGroup group(cx, obj->getGroup(cx));
if (!group)
return false;
- if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, obj)) {
+ if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, nobj)) {
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_DenseAdd stub "
"(shape=%p, group=%p, protoDepth=%u)",
- obj->lastProperty(), group.get(), protoDepth);
+ nobj->lastProperty(), group.get(), protoDepth);
ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth);
ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
if (!denseStub)
return false;
if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
return false;
stub->addNewStub(denseStub);
} else if (!addingCase &&
- !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, obj))
+ !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, nobj))
{
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_Dense stub (shape=%p, group=%p)",
- obj->lastProperty(), group.get());
+ nobj->lastProperty(), group.get());
ICSetElem_Dense::Compiler compiler(cx, shape, group);
ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
if (!denseStub)
return false;
if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
return false;
stub->addNewStub(denseStub);
@@ -5166,17 +5177,17 @@ DoSetElemFallback(JSContext *cx, Baselin
return true;
}
if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
// Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
if (expectOutOfBounds)
RemoveExistingTypedArraySetElemStub(cx, stub, obj);
- Shape *shape = obj->lastProperty();
+ Shape *shape = obj->maybeShape();
Scalar::Type type = TypedThingElementType(obj);
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
shape, type, expectOutOfBounds ? "yes" : "no");
ICSetElem_TypedArray::Compiler compiler(cx, shape, type, expectOutOfBounds);
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
@@ -5368,33 +5379,33 @@ ICSetElem_Dense::Compiler::generateStubC
return true;
}
static bool
GetProtoShapes(JSObject *obj, size_t protoChainDepth, AutoShapeVector *shapes)
{
JSObject *curProto = obj->getProto();
for (size_t i = 0; i < protoChainDepth; i++) {
- if (!shapes->append(curProto->lastProperty()))
+ if (!shapes->append(curProto->as<NativeObject>().lastProperty()))
return false;
curProto = curProto->getProto();
}
MOZ_ASSERT(!curProto);
return true;
}
//
// SetElem_DenseAdd
//
ICUpdatedStub *
ICSetElemDenseAddCompiler::getStub(ICStubSpace *space)
{
AutoShapeVector shapes(cx);
- if (!shapes.append(obj_->lastProperty()))
+ if (!shapes.append(obj_->as<NativeObject>().lastProperty()))
return nullptr;
if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
return nullptr;
JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4);
ICUpdatedStub *stub = nullptr;
@@ -5757,17 +5768,17 @@ ICIn_Fallback::Compiler::generateStubCod
// given holder in place with a new shape and getter. fallbackStub can be
// either an ICGetProp_Fallback or an ICGetName_Fallback.
//
// When kind == ICStub::GetProp_CallNative, callers should pass a null receiver,
// since in that case receiver and holder are the same thing.
static bool
UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub,
ICStub::Kind kind,
- HandleObject holder,
+ HandleNativeObject holder,
HandleObject receiver,
HandleFunction getter)
{
MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
kind == ICStub::GetProp_CallNative ||
kind == ICStub::GetProp_CallNativePrototype);
MOZ_ASSERT(fallbackStub->isGetName_Fallback() ||
fallbackStub->isGetProp_Fallback());
@@ -5810,17 +5821,17 @@ UpdateExistingGetPropCallStubs(ICFallbac
return foundMatchingStub;
}
// Try to update existing SetProp setter call stubs for the given holder in
// place with a new shape and setter.
static bool
UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
ICStub::Kind kind,
- JSObject *holder,
+ NativeObject *holder,
ReceiverGuard::Token receiverGuard,
JSFunction *setter)
{
MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
kind == ICStub::SetProp_CallNative);
bool foundMatchingStub = false;
for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
if (iter->kind() == kind) {
@@ -5958,17 +5969,17 @@ TryAttachScopeNameStub(JSContext *cx, Ha
MOZ_ASSERT(!*attached);
AutoShapeVector shapes(cx);
RootedId id(cx, NameToId(name));
RootedObject scopeChain(cx, initialScopeChain);
Shape *shape = nullptr;
while (scopeChain) {
- if (!shapes.append(scopeChain->lastProperty()))
+ if (!shapes.append(scopeChain->maybeShape()))
return false;
if (scopeChain->is<GlobalObject>()) {
shape = scopeChain->as<GlobalObject>().lookup(cx, id);
if (shape)
break;
return true;
}
@@ -6487,17 +6498,17 @@ TryAttachNativeGetPropStub(JSContext *cx
MOZ_ASSERT(!*attached);
MOZ_ASSERT(!*isTemporarilyUnoptimizable);
if (!val.isObject())
return true;
RootedObject obj(cx, &val.toObject());
- if (oldShape != obj->lastProperty()) {
+ if (obj->isNative() && oldShape != obj->as<NativeObject>().lastProperty()) {
// No point attaching anything, since we know the shape guard will fail
return true;
}
bool isDOMProxy;
bool domProxyHasGeneration;
DOMProxyShadowsResult domProxyShadowsResult;
RootedShape shape(cx);
@@ -6560,17 +6571,17 @@ TryAttachNativeGetPropStub(JSContext *cx
if (obj == holder)
return true;
RootedFunction callee(cx, &shape->getterObject()->as<JSFunction>());
MOZ_ASSERT(obj != holder);
MOZ_ASSERT(callee->hasScript());
if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallScripted,
- holder, obj, callee)) {
+ holder.as<NativeObject>(), obj, callee)) {
*attached = true;
return true;
}
JitSpew(JitSpew_BaselineIC, " Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub",
callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee,
@@ -6666,27 +6677,27 @@ TryAttachNativeGetPropStub(JSContext *cx
kind = ICStub::GetProp_CallDOMProxyNative;
}
Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
ICGetPropCallDOMProxyNativeCompiler compiler(cx, kind, monitorStub, proxy, holder, callee,
script->pcToOffset(pc));
newStub = compiler.getStub(compiler.getStubSpace(script));
} else if (obj == holder) {
if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative,
- obj, JS::NullPtr(), callee)) {
+ obj.as<NativeObject>(), JS::NullPtr(), callee)) {
*attached = true;
return true;
}
ICGetProp_CallNative::Compiler compiler(cx, monitorStub, obj, callee,
script->pcToOffset(pc), outerClass);
newStub = compiler.getStub(compiler.getStubSpace(script));
} else {
if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativePrototype,
- holder, obj, callee)) {
+ holder.as<NativeObject>(), obj, callee)) {
*attached = true;
return true;
}
ICGetProp_CallNativePrototype::Compiler compiler(cx, monitorStub, obj, holder, callee,
script->pcToOffset(pc), outerClass);
newStub = compiler.getStub(compiler.getStubSpace(script));
}
@@ -6755,17 +6766,17 @@ TryAttachTypedObjectGetPropStub(JSContex
Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
if (!fieldDescr->is<SimpleTypeDescr>())
return true;
uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
- ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->lastProperty(),
+ ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->maybeShape(),
fieldOffset, &fieldDescr->as<SimpleTypeDescr>());
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
*attached = true;
return true;
@@ -6918,17 +6929,17 @@ DoGetPropFallback(JSContext *cx, Baselin
JSOp op = JSOp(*pc);
FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]);
MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP);
// Grab our old shape before it goes away.
RootedShape oldShape(cx);
if (val.isObject())
- oldShape = val.toObject().lastProperty();
+ oldShape = val.toObject().maybeShape();
// After the Genericstub was added, we should never reach the Fallbackstub again.
MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic));
RootedPropertyName name(cx, frame->script()->getName(pc));
if (!ComputeGetPropResult(cx, frame, op, name, val, res))
return false;
@@ -7162,24 +7173,24 @@ ICGetProp_Primitive::Compiler::generateS
}
ICGetPropNativeStub *
ICGetPropNativeCompiler::getStub(ICStubSpace *space)
{
switch (kind) {
case ICStub::GetProp_Native: {
MOZ_ASSERT(obj_ == holder_);
- RootedShape shape(cx, obj_->lastProperty());
+ RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, shape, offset_);
}
case ICStub::GetProp_NativePrototype: {
MOZ_ASSERT(obj_ != holder_);
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
- Shape *holderShape = holder_->lastProperty();
+ Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, guard,
offset_, holder_, holderShape);
}
default:
MOZ_CRASH("Bad stub kind");
}
}
@@ -7686,18 +7697,18 @@ ICGetPropCallDOMProxyNativeCompiler::gen
Address generationAddress(BaselineStubReg,
ICGetProp_CallDOMProxyWithGenerationNative::offsetOfGeneration());
return generateStubCode(masm, &internalStructAddress, &generationAddress);
}
ICStub *
ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace *space)
{
- RootedShape shape(cx, proxy_->lastProperty());
- RootedShape holderShape(cx, holder_->lastProperty());
+ RootedShape shape(cx, proxy_->maybeShape());
+ RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot());
RootedShape expandoShape(cx, nullptr);
ExpandoAndGeneration *expandoAndGeneration;
int32_t generation;
Value expandoVal;
if (kind == ICStub::GetProp_CallDOMProxyNative) {
expandoVal = expandoSlot;
@@ -7707,34 +7718,34 @@ ICGetPropCallDOMProxyNativeCompiler::get
MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative);
MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
expandoVal = expandoAndGeneration->expando;
generation = expandoAndGeneration->generation;
}
if (expandoVal.isObject())
- expandoShape = expandoVal.toObject().lastProperty();
+ expandoShape = expandoVal.toObject().as<NativeObject>().lastProperty();
if (kind == ICStub::GetProp_CallDOMProxyNative) {
return ICStub::New<ICGetProp_CallDOMProxyNative>(
space, getStubCode(), firstMonitorStub_, shape,
expandoShape, holder_, holderShape, getter_, pcOffset_);
}
return ICStub::New<ICGetProp_CallDOMProxyWithGenerationNative>(
space, getStubCode(), firstMonitorStub_, shape,
expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_,
pcOffset_);
}
ICStub *
ICGetProp_DOMProxyShadowed::Compiler::getStub(ICStubSpace *space)
{
- RootedShape shape(cx, proxy_->lastProperty());
+ RootedShape shape(cx, proxy_->maybeShape());
return New<ICGetProp_DOMProxyShadowed>(space, getStubCode(), firstMonitorStub_, shape,
proxy_->handler(), name_, pcOffset_);
}
static bool
ProxyGet(JSContext *cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp)
{
RootedId id(cx, NameToId(name));
@@ -8110,17 +8121,17 @@ TryAttachSetValuePropStub(JSContext *cx,
return true;
}
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObject.PROP) stub");
- MOZ_ASSERT(obj->lastProperty() == oldShape,
+ MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
"Should this really be a SetPropWriteSlot?");
ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
return false;
@@ -8162,17 +8173,17 @@ TryAttachSetAccessorPropStub(JSContext *
// Try handling scripted setters.
if (cacheableCall && isScripted) {
RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
MOZ_ASSERT(obj != holder);
MOZ_ASSERT(callee->hasScript());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
- holder, receiverGuard, callee)) {
+ &holder->as<NativeObject>(), receiverGuard, callee)) {
*attached = true;
return true;
}
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8187,17 +8198,17 @@ TryAttachSetAccessorPropStub(JSContext *
// Try handling JSNative setters.
if (cacheableCall && !isScripted) {
RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
MOZ_ASSERT(obj != holder);
MOZ_ASSERT(callee->isNative());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
- holder, receiverGuard, callee)) {
+ &holder->as<NativeObject>(), receiverGuard, callee)) {
*attached = true;
return true;
}
JitSpew(JitSpew_BaselineIC, " Generating SetProp(NativeObj/NativeSetter %p) stub",
callee->native());
ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8270,17 +8281,17 @@ TryAttachTypedObjectSetPropStub(JSContex
return true;
Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
if (!fieldDescr->is<SimpleTypeDescr>())
return true;
uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
- ICSetProp_TypedObject::Compiler compiler(cx, obj->lastProperty(), obj->group(), fieldOffset,
+ ICSetProp_TypedObject::Compiler compiler(cx, obj->maybeShape(), obj->group(), fieldOffset,
&fieldDescr->as<SimpleTypeDescr>());
ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
return false;
stub->addNewStub(newStub);
@@ -8319,17 +8330,17 @@ DoSetPropFallback(JSContext *cx, Baselin
else
name = script->getName(pc);
RootedId id(cx, NameToId(name));
RootedObject obj(cx, ToObjectFromStack(cx, lhs));
if (!obj)
return false;
ReceiverGuard::Token oldGuard = ReceiverGuard::objectToken(obj);
- RootedShape oldShape(cx, obj->lastProperty());
+ RootedShape oldShape(cx, obj->maybeShape());
RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
if (!oldGroup)
return false;
uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
// We want to avoid calling noteUnoptimizableAccess() if the reason we
@@ -11978,17 +11989,17 @@ ICSetProp_Native::ICSetProp_Native(JitCo
ICSetProp_Native *
ICSetProp_Native::Compiler::getStub(ICStubSpace *space)
{
RootedObjectGroup group(cx, obj_->getGroup(cx));
if (!group)
return nullptr;
- RootedShape shape(cx, obj_->lastProperty());
+ RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
if (!stub || !stub->initUpdatingChain(cx, space))
return nullptr;
return stub;
}
ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
size_t protoChainDepth,
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -2916,26 +2916,26 @@ class ICGetElemNativeCompiler : public I
acctype_(acctype),
needsAtomize_(needsAtomize),
offset_(0),
getter_(getter),
pcOffset_(pcOffset)
{}
ICStub *getStub(ICStubSpace *space) {
- RootedShape shape(cx, obj_->lastProperty());
+ RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
if (kind == ICStub::GetElem_NativeSlot) {
MOZ_ASSERT(obj_ == holder_);
return ICStub::New<ICGetElem_NativeSlot>(
space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_,
offset_);
}
MOZ_ASSERT(obj_ != holder_);
- RootedShape holderShape(cx, holder_->lastProperty());
+ RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
if (kind == ICStub::GetElem_NativePrototypeSlot) {
return ICStub::New<ICGetElem_NativePrototypeSlot>(
space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_,
offset_, holder_, holderShape);
}
if (kind == ICStub::GetElem_NativePrototypeCallNative) {
return ICStub::New<ICGetElem_NativePrototypeCallNative>(
@@ -3807,17 +3807,17 @@ class ICGetProp_Primitive : public ICMon
firstMonitorStub_(firstMonitorStub),
primitiveType_(primitiveType),
prototype_(cx, prototype),
isFixedSlot_(isFixedSlot),
offset_(offset)
{}
ICStub *getStub(ICStubSpace *space) {
- RootedShape protoShape(cx, prototype_->lastProperty());
+ RootedShape protoShape(cx, prototype_->as<NativeObject>().lastProperty());
return ICStub::New<ICGetProp_Primitive>(space, getStubCode(), firstMonitorStub_,
protoShape, offset_);
}
};
};
// Stub for accessing a string's length.
class ICGetProp_StringLength : public ICStub
@@ -3924,17 +3924,17 @@ class ReceiverGuard
if (!(token & 1))
return reinterpret_cast<ObjectGroup *>(token);
return nullptr;
}
static Token objectToken(JSObject *obj) {
if (obj->is<UnboxedPlainObject>())
return groupToken(obj->group());
- return shapeToken(obj->lastProperty());
+ return shapeToken(obj->maybeShape());
}
explicit ReceiverGuard(Token token)
: shape_(tokenShape(token)), group_(tokenGroup(token))
{
MOZ_ASSERT(shape_ || group_);
}
@@ -4416,17 +4416,17 @@ class ICGetProp_CallScripted : public IC
HandleObject holder, HandleFunction getter, uint32_t pcOffset)
: ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallScripted,
firstMonitorStub, obj, holder,
getter, pcOffset, /* outerClass = */ nullptr)
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
- Shape *holderShape = holder_->lastProperty();
+ Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
guard, holder_, holderShape, getter_,
pcOffset_);
}
};
};
// Stub for calling an own native getter on a native object.
@@ -4462,17 +4462,17 @@ class ICGetProp_CallNative : public ICGe
HandleFunction getter, uint32_t pcOffset, const Class *outerClass,
bool inputDefinitelyObject = false)
: ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallNative, firstMonitorStub,
obj, getter, pcOffset, outerClass, true),
inputDefinitelyObject_(inputDefinitelyObject)
{}
ICStub *getStub(ICStubSpace *space) {
- RootedShape shape(cx, holder_->lastProperty());
+ RootedShape shape(cx, holder_->as<NativeObject>().lastProperty());
return ICStub::New<ICGetProp_CallNative>(space, getStubCode(), firstMonitorStub_,
holder_, shape, getter_, pcOffset_);
}
};
};
// Stub for calling an native getter on a native object when the getter is kept on the proto-chain.
class ICGetProp_CallNativePrototype : public ICGetPropCallPrototypeGetter
@@ -4509,17 +4509,17 @@ class ICGetProp_CallNativePrototype : pu
: ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallNativePrototype,
firstMonitorStub, obj, holder,
getter, pcOffset, outerClass),
inputDefinitelyObject_(inputDefinitelyObject)
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
- Shape *holderShape = holder_->lastProperty();
+ Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICGetProp_CallNativePrototype>(space, getStubCode(), firstMonitorStub_,
guard, holder_, holderShape,
getter_, pcOffset_);
}
};
};
class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter
@@ -4959,17 +4959,17 @@ class ICSetPropNativeAddCompiler : publi
return nullptr;
// Only specify newGroup when the object's group changes due to the
// object becoming fully initialized per the acquired properties
// analysis.
if (newGroup == oldGroup_)
newGroup = nullptr;
- RootedShape newShape(cx, obj_->lastProperty());
+ RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
}
ICUpdatedStub *getStub(ICStubSpace *space);
};
@@ -5225,17 +5225,17 @@ class ICSetProp_CallScripted : public IC
Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
uint32_t pcOffset)
: ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted,
obj, holder, setter, pcOffset)
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
- Shape *holderShape = holder_->lastProperty();
+ Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
holderShape, setter_, pcOffset_);
}
};
};
// Stub for calling a native setter on a native object.
class ICSetProp_CallNative : public ICSetPropCallSetter
@@ -5261,17 +5261,17 @@ class ICSetProp_CallNative : public ICSe
Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
uint32_t pcOffset)
: ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative,
obj, holder, setter, pcOffset)
{}
ICStub *getStub(ICStubSpace *space) {
ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
- Shape *holderShape = holder_->lastProperty();
+ Shape *holderShape = holder_->as<NativeObject>().lastProperty();
return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), guard, holder_,
holderShape, setter_, pcOffset_);
}
};
};
// Call
// JSOP_CALL
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4594,17 +4594,17 @@ CodeGenerator::visitNewSingletonCallObje
Register objReg = ToRegister(lir->output());
JSObject *templateObj = lir->mir()->templateObject();
JSScript *script = lir->mir()->block()->info().script();
uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin();
OutOfLineCode *ool;
ool = oolCallVM(NewSingletonCallObjectInfo, lir,
- (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+ (ArgList(), ImmGCPtr(templateObj->as<CallObject>().lastProperty()),
Imm32(lexicalBegin)),
StoreRegisterTo(objReg));
// Objects can only be given singleton types in VM calls. We make the call
// out of line to not bloat inline code, even if (naively) this seems like
// extra work.
masm.jump(ool->entry());
masm.bind(ool->rejoin());
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1772,17 +1772,17 @@ OffThreadCompilationAvailable(JSContext
&& CanUseExtraThreads();
}
static void
TrackAllProperties(JSContext *cx, JSObject *obj)
{
MOZ_ASSERT(obj->isSingleton());
- for (Shape::Range<NoGC> range(obj->lastProperty()); !range.empty(); range.popFront())
+ for (Shape::Range<NoGC> range(obj->as<NativeObject>().lastProperty()); !range.empty(); range.popFront())
EnsureTrackPropertyTypes(cx, obj, range.front().propid());
}
static void
TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame)
{
// Ensure that all properties of singleton call objects which the script
// could access are tracked. These are generally accessed through
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -770,17 +770,17 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
MOZ_ASSERT(!expandoVal.toObject().as<NativeObject>().contains(cx, name));
// Reference object has an expando object that doesn't define the name. Check that
// the incoming object has an expando object with the same shape.
masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck);
masm.extractObject(tempVal, tempVal.scratchReg());
masm.branchPtr(Assembler::Equal,
Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
- ImmGCPtr(expandoVal.toObject().lastProperty()),
+ ImmGCPtr(expandoVal.toObject().as<NativeObject>().lastProperty()),
&domProxyOk);
}
// Failure case: restore the tempVal registers and jump to failures.
masm.bind(&failDOMProxyCheck);
masm.popValue(tempVal);
masm.jump(stubFailure);
@@ -805,17 +805,17 @@ GenerateReadSlot(JSContext *cx, IonScrip
Label failures_;
if (multipleFailureJumps && !failures)
failures = &failures_;
// Guard on the shape or type of the object, depending on whether it is native.
if (obj->isNative()) {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()),
+ ImmGCPtr(obj->as<NativeObject>().lastProperty()),
failures);
} else {
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfGroup()),
ImmGCPtr(obj->group()),
failures);
}
@@ -869,17 +869,17 @@ GenerateReadSlot(JSContext *cx, IonScrip
Register lastReg = object;
MOZ_ASSERT(scratchReg != object);
while (proto) {
masm.loadObjProto(lastReg, scratchReg);
// Guard the shape of the current prototype.
masm.branchPtr(Assembler::NotEqual,
Address(scratchReg, JSObject::offsetOfShape()),
- ImmGCPtr(proto->lastProperty()),
+ ImmGCPtr(proto->as<NativeObject>().lastProperty()),
&prototypeFailures);
proto = proto->getProto();
lastReg = scratchReg;
}
holderReg = InvalidReg;
}
@@ -1093,35 +1093,38 @@ EmitGetterCall(JSContext *cx, MacroAssem
masm.freeStack(masm.framePushed() - framePushedBefore);
}
masm.icRestoreLive(liveRegs, aic);
return true;
}
+static void
+TestMatchingReceiver(MacroAssembler &masm, Register object, JSObject *obj, Label *failure)
+{
+ if (Shape *shape = obj->maybeShape())
+ masm.branchTestObjShape(Assembler::NotEqual, object, shape, failure);
+ else
+ masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
+}
+
static bool
GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
TypedOrValueRegister output, void *returnAddr, Label *failures = nullptr)
{
MOZ_ASSERT(output.hasValue());
// Use the passed in label if there was one. Otherwise, we'll have to make our own.
Label stubFailure;
failures = failures ? failures : &stubFailure;
- // Initial shape/group check.
- if (obj->isNative())
- masm.branchTestObjShape(Assembler::NotEqual, object, obj->lastProperty(), failures);
- else if (obj->is<UnboxedPlainObject>())
- masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failures);
- else
- MOZ_CRASH("Unexpected object");
+ TestMatchingReceiver(masm, object, obj, failures);
Register scratchReg = output.valueReg().scratchReg();
bool spillObjReg = scratchReg == object;
Label pop1AndFail;
Label *maybePopAndFail = failures;
// Save off the object register if it aliases the scratchReg
if (spillObjReg) {
@@ -1133,17 +1136,17 @@ GenerateCallGetter(JSContext *cx, IonScr
if (obj != holder)
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
// Guard on the holder's shape.
Register holderReg = scratchReg;
masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
masm.branchPtr(Assembler::NotEqual,
Address(holderReg, JSObject::offsetOfShape()),
- ImmGCPtr(holder->lastProperty()),
+ ImmGCPtr(holder->as<NativeObject>().lastProperty()),
maybePopAndFail);
if (spillObjReg)
masm.pop(object);
// Now we're good to go to invoke the native call.
if (!EmitGetterCall(cx, masm, attacher, obj, holder, shape, liveRegs, object,
output, returnAddr))
@@ -1167,17 +1170,17 @@ static bool
GenerateArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, Register object, TypedOrValueRegister output)
{
MOZ_ASSERT(obj->is<ArrayObject>());
Label failures;
// Guard object is a dense array.
- RootedShape shape(cx, obj->lastProperty());
+ RootedShape shape(cx, obj->as<ArrayObject>().lastProperty());
if (!shape)
return false;
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
// Load length.
Register outReg;
if (output.hasValue()) {
outReg = output.valueReg().scratchReg();
@@ -1551,17 +1554,17 @@ GetPropertyIC::tryAttachDOMProxyShadowed
Label failures;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()),
+ ImmGCPtr(obj->maybeShape()),
&failures);
// No need for more guards: we know this is a DOM proxy, since the shape
// guard enforces a given JSClass, so just go ahead and emit the call to
// ProxyGet.
if (!EmitCallProxyGet(cx, masm, attacher, name(), liveRegs_, object(), output(),
pc(), returnAddr))
@@ -1620,17 +1623,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
Label failures;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()),
+ ImmGCPtr(obj->maybeShape()),
&failures);
// Guard that our expando object hasn't started shadowing this property.
CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, name, object(), &failures);
if (holder) {
// Found the property on the prototype chain. Treat it like a native
// getprop.
@@ -2234,17 +2237,17 @@ SetPropertyIC::attachDOMProxyShadowed(JS
Label failures;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
masm.branchPtr(Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()), &failures);
+ ImmGCPtr(obj->maybeShape()), &failures);
// No need for more guards: we know this is a DOM proxy, since the shape
// guard enforces a given JSClass, so just go ahead and emit the call to
// ProxySet.
RootedId propId(cx, AtomToId(name()));
if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
value(), returnAddr, strict()))
@@ -2284,17 +2287,17 @@ GenerateCallSetter(JSContext *cx, IonScr
// Generate prototype/shape guards.
if (obj != holder)
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure);
masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg);
masm.branchPtr(Assembler::NotEqual,
Address(scratchReg, JSObject::offsetOfShape()),
- ImmGCPtr(holder->lastProperty()),
+ ImmGCPtr(holder->as<NativeObject>().lastProperty()),
&protoFailure);
masm.jump(&protoSuccess);
masm.bind(&protoFailure);
masm.pop(scratchReg);
masm.jump(failure);
@@ -2508,17 +2511,17 @@ SetPropertyIC::attachDOMProxyUnshadowed(
Label failures;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the object.
masm.branchPtr(Assembler::NotEqual,
Address(object(), JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()), &failures);
+ ImmGCPtr(obj->maybeShape()), &failures);
// Guard that our expando object hasn't started shadowing this property.
CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, name(), object(), &failures);
RootedPropertyName propName(cx, name());
RootedObject holder(cx);
RootedShape shape(cx);
bool isSetter;
@@ -2559,22 +2562,17 @@ bool
SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj, HandleObject holder, HandleShape shape,
void *returnAddr)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
Label failure;
- if (obj->isNative())
- masm.branchTestObjShape(Assembler::NotEqual, object(), obj->lastProperty(), &failure);
- else if (obj->is<UnboxedPlainObject>())
- masm.branchTestObjGroup(Assembler::NotEqual, object(), obj->group(), &failure);
- else
- MOZ_CRASH("Unexpected object");
+ TestMatchingReceiver(masm, object(), obj, &failure);
if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
object(), value(), &failure, liveRegs_, returnAddr))
{
return false;
}
// Rejoin jump.
@@ -2612,17 +2610,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse
if (checkTypeset) {
CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject);
masm.loadPtr(Address(StackPointer, 0), object);
}
JSObject *proto = obj->getProto();
Register protoReg = object;
while (proto) {
- Shape *protoShape = proto->lastProperty();
+ Shape *protoShape = proto->as<NativeObject>().lastProperty();
// load next prototype
masm.loadObjProto(protoReg, protoReg);
// Ensure that its shape matches.
masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &failuresPopObject);
proto = proto->getProto();
@@ -3021,17 +3019,17 @@ SetPropertyIC::update(JSContext *cx, Han
{
return false;
}
addedSetterStub = true;
}
}
uint32_t oldSlots = obj->is<NativeObject>() ? obj->as<NativeObject>().numDynamicSlots() : 0;
- RootedShape oldShape(cx, obj->lastProperty());
+ RootedShape oldShape(cx, obj->maybeShape());
// Set/Add the property on the object, the inlined cache are setup for the next execution.
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
return false;
// The property did not exist before, now we can try to inline the property add.
bool checkTypeset;
if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
@@ -3190,17 +3188,17 @@ GenerateDenseElement(JSContext *cx, Macr
JSObject *obj, const Value &idval, Register object,
ConstantOrRegister index, TypedOrValueRegister output)
{
MOZ_ASSERT(GetElementIC::canAttachDenseElement(obj, idval));
Label failures;
// Guard object's shape.
- RootedShape shape(cx, obj->lastProperty());
+ RootedShape shape(cx, obj->as<NativeObject>().lastProperty());
if (!shape)
return false;
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
// Ensure the index is an int32 value.
Register indexReg = InvalidReg;
if (index.reg().hasValue()) {
@@ -3298,48 +3296,47 @@ GetElementIC::canAttachDenseElementHole(
}
static bool
GenerateDenseElementHole(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
IonScript *ion, JSObject *obj, const Value &idval,
Register object, ConstantOrRegister index, TypedOrValueRegister output)
{
MOZ_ASSERT(GetElementIC::canAttachDenseElementHole(obj, idval, output));
- MOZ_ASSERT(obj->lastProperty());
Register scratchReg = output.valueReg().scratchReg();
// Guard on the shape and group, to prevent non-dense elements from appearing.
Label failures;
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(object, JSObject::offsetOfShape()),
- ImmGCPtr(obj->lastProperty()), &failures);
+ ImmGCPtr(obj->as<NativeObject>().lastProperty()), &failures);
if (obj->hasUncacheableProto()) {
masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg);
Address proto(scratchReg, ObjectGroup::offsetOfProto());
masm.branchPtr(Assembler::NotEqual, proto,
ImmMaybeNurseryPtr(obj->getProto()), &failures);
}
JSObject *pobj = obj->getProto();
while (pobj) {
- MOZ_ASSERT(pobj->lastProperty());
+ MOZ_ASSERT(pobj->as<NativeObject>().lastProperty());
masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
if (pobj->hasUncacheableProto()) {
MOZ_ASSERT(!pobj->isSingleton());
Address groupAddr(scratchReg, JSObject::offsetOfGroup());
masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), &failures);
}
// Make sure the shape matches, to avoid non-dense elements.
masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()),
- ImmGCPtr(pobj->lastProperty()), &failures);
+ ImmGCPtr(pobj->as<NativeObject>().lastProperty()), &failures);
// Load elements vector.
masm.loadPtr(Address(scratchReg, NativeObject::offsetOfElements()), scratchReg);
// Also make sure there are no dense elements.
Label hole;
Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
masm.branch32(Assembler::NotEqual, initLength, Imm32(0), &failures);
@@ -3867,17 +3864,17 @@ GenerateSetDenseElement(JSContext *cx, M
MOZ_ASSERT(idval.isInt32());
Label failures;
Label outOfBounds; // index represents a known hole, or an illegal append
Label markElem, storeElement; // used if TI protects us from worrying about holes.
// Guard object is a dense array.
- Shape *shape = obj->lastProperty();
+ Shape *shape = obj->as<NativeObject>().lastProperty();
if (!shape)
return false;
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
// Ensure the index is an int32 value.
masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
// Unbox the index.
@@ -4143,17 +4140,18 @@ GenerateScopeChainGuard(MacroAssembler &
// If this is the last object on the scope walk, and the property we've
// found is not configurable, then we don't need a shape guard because
// the shape cannot be removed.
if (shape && !shape->configurable())
return;
}
Address shapeAddr(scopeObjReg, JSObject::offsetOfShape());
- masm.branchPtr(Assembler::NotEqual, shapeAddr, ImmGCPtr(scopeObj->lastProperty()), failures);
+ masm.branchPtr(Assembler::NotEqual, shapeAddr,
+ ImmGCPtr(scopeObj->as<NativeObject>().lastProperty()), failures);
}
static void
GenerateScopeChainGuards(MacroAssembler &masm, JSObject *scopeChain, JSObject *holder,
Register outputReg, Label *failures, bool skipLastGuard = false)
{
JSObject *tobj = scopeChain;
@@ -4184,17 +4182,17 @@ BindNameIC::attachNonGlobal(JSContext *c
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
// Guard on the shape of the scope chain.
Label failures;
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
Address(scopeChainReg(), JSObject::offsetOfShape()),
- ImmGCPtr(scopeChain->lastProperty()),
+ ImmGCPtr(scopeChain->as<NativeObject>().lastProperty()),
holder != scopeChain ? &failures : nullptr);
if (holder != scopeChain) {
JSObject *parent = &scopeChain->as<ScopeObject>().enclosingScope();
masm.extractObject(Address(scopeChainReg(), ScopeObject::offsetOfEnclosingScope()), outputReg());
GenerateScopeChainGuards(masm, parent, holder, outputReg(), &failures);
} else {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1214,19 +1214,21 @@ MacroAssembler::initGCSlots(Register obj
}
void
MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
bool initFixedSlots)
{
// Fast initialization of an empty object returned by allocateObject().
- storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape()));
storePtr(ImmGCPtr(templateObj->group()), Address(obj, JSObject::offsetOfGroup()));
+ if (Shape *shape = templateObj->maybeShape())
+ storePtr(ImmGCPtr(shape), Address(obj, JSObject::offsetOfShape()));
+
if (templateObj->isNative()) {
NativeObject *ntemplate = &templateObj->as<NativeObject>();
MOZ_ASSERT_IF(!ntemplate->denseElementsAreCopyOnWrite(), !ntemplate->hasDynamicElements());
if (ntemplate->hasDynamicSlots())
storePtr(slots, Address(obj, NativeObject::offsetOfSlots()));
else
storePtr(ImmPtr(nullptr), Address(obj, NativeObject::offsetOfSlots()));
@@ -1275,16 +1277,18 @@ MacroAssembler::initGCThing(Register obj
storePtr(ImmWord(value),
Address(obj, InlineTypedObject::offsetOfDataStart() + offset));
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
offset += sizeof(uintptr_t);
}
} else if (templateObj->is<UnboxedPlainObject>()) {
const UnboxedLayout &layout = templateObj->as<UnboxedPlainObject>().layout();
+ storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
+
// Initialize reference fields of the object, per UnboxedPlainObject::create.
if (const int32_t *list = layout.traceList()) {
while (*list != -1) {
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
Address(obj, UnboxedPlainObject::offsetOfData() + *list));
list++;
}
list++;
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -163,17 +163,17 @@ IsObjectEscaped(MInstruction *ins, JSObj
}
#endif
break;
}
case MDefinition::Op_GuardShape: {
MGuardShape *guard = def->toGuardShape();
MOZ_ASSERT(!ins->isGuardShape());
- if (obj->lastProperty() != guard->shape()) {
+ if (obj->as<NativeObject>().lastProperty() != guard->shape()) {
JitSpewDef(JitSpew_Escape, "Object ", ins);
JitSpewDef(JitSpew_Escape, " has a non-matching guard shape\n", guard);
return true;
}
if (IsObjectEscaped(def->toInstruction(), obj))
return true;
break;
}
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1138,18 +1138,18 @@ AutoDetectInvalidation::setReturnOverrid
void
AssertValidObjectPtr(JSContext *cx, JSObject *obj)
{
// Check what we can, so that we'll hopefully assert/crash if we get a
// bogus object (pointer).
MOZ_ASSERT(obj->compartment() == cx->compartment());
MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
- MOZ_ASSERT_IF(!obj->hasLazyGroup(),
- obj->group()->clasp() == obj->lastProperty()->getObjectClass());
+ MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
+ obj->group()->clasp() == obj->maybeShape()->getObjectClass());
if (obj->isTenured()) {
MOZ_ASSERT(obj->isAligned());
gc::AllocKind kind = obj->asTenured().getAllocKind();
MOZ_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
}
}
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2749,17 +2749,17 @@ GetIndexedPropertiesInRange(JSContext *c
for (uint32_t i = begin; i < len && i < end; i++) {
if (!indexes.append(i))
return false;
}
}
// Append sparse elements.
if (pobj->isIndexed()) {
- Shape::Range<NoGC> r(pobj->lastProperty());
+ Shape::Range<NoGC> r(pobj->as<NativeObject>().lastProperty());
for (; !r.empty(); r.popFront()) {
Shape &shape = r.front();
jsid id = shape.propid();
if (!JSID_IS_INT(id))
continue;
uint32_t i = uint32_t(JSID_TO_INT(id));
if (!(begin <= i && i < end))
@@ -3530,17 +3530,17 @@ js::NewDenseCopiedArray(JSContext *cx, u
ArrayObject *
js::NewDenseFullyAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject)
{
gc::AllocKind allocKind = GuessArrayGCKind(length);
MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
allocKind = GetBackgroundAllocKind(allocKind);
RootedObjectGroup group(cx, templateObject->group());
- RootedShape shape(cx, templateObject->lastProperty());
+ RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
Rooted<ArrayObject *> arr(cx, ArrayObject::createArray(cx, allocKind,
heap, shape, group, length));
if (!arr)
return nullptr;
if (!EnsureNewArrayElements(cx, arr, length))
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -316,20 +316,20 @@ js::IsScopeObject(JSObject *obj)
}
JS_FRIEND_API(bool)
js::IsCallObject(JSObject *obj)
{
return obj->is<CallObject>();
}
-JS_FRIEND_API(JSObject *)
-js::GetObjectParentMaybeScope(JSObject *obj)
+JS_FRIEND_API(bool)
+js::CanAccessObjectShape(JSObject *obj)
{
- return obj->enclosingScope();
+ return obj->maybeShape() != nullptr;
}
JS_FRIEND_API(JSObject *)
js::GetGlobalForObjectCrossCompartment(JSObject *obj)
{
return &obj->global();
}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -558,18 +558,19 @@ class Shape {
public:
shadow::BaseShape *base;
jsid _1;
uint32_t slotInfo;
static const uint32_t FIXED_SLOTS_SHIFT = 27;
};
-// This layout is shared by all objects except for Typed Objects (which still
-// have a shape and group).
+// This layout is shared by all native objects. For non-native objects, the
+// group may always be accessed safely, and other members may be as well,
+// depending on the object's specific layout.
struct Object {
shadow::ObjectGroup *group;
shadow::Shape *shape;
JS::Value *slots;
void *_1;
size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
JS::Value *fixedSlots() const {
@@ -678,33 +679,34 @@ JS_FRIEND_API(bool)
IsFunctionObject(JSObject *obj);
JS_FRIEND_API(bool)
IsScopeObject(JSObject *obj);
JS_FRIEND_API(bool)
IsCallObject(JSObject *obj);
+JS_FRIEND_API(bool)
+CanAccessObjectShape(JSObject *obj);
+
inline JSObject *
GetObjectParent(JSObject *obj)
{
MOZ_ASSERT(!IsScopeObject(obj));
+ MOZ_ASSERT(CanAccessObjectShape(obj));
return reinterpret_cast<shadow::Object*>(obj)->shape->base->parent;
}
static MOZ_ALWAYS_INLINE JSCompartment *
GetObjectCompartment(JSObject *obj)
{
return reinterpret_cast<shadow::Object*>(obj)->group->compartment;
}
JS_FRIEND_API(JSObject *)
-GetObjectParentMaybeScope(JSObject *obj);
-
-JS_FRIEND_API(JSObject *)
GetGlobalForObjectCrossCompartment(JSObject *obj);
JS_FRIEND_API(JSObject *)
GetPrototypeNoProxy(JSObject *obj);
// Sidestep the activeContext checking implicitly performed in
// JS_SetPendingException.
JS_FRIEND_API(void)
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -505,18 +505,18 @@ CheckIncrementalZoneState(ExclusiveConte
template <AllowGC allowGC>
inline JSObject *
AllocateObject(ExclusiveContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap,
const js::Class *clasp)
{
size_t thingSize = Arena::thingSize(kind);
MOZ_ASSERT(thingSize == Arena::thingSize(kind));
- MOZ_ASSERT(thingSize >= sizeof(JSObject));
- static_assert(sizeof(JSObject) >= CellSize,
+ MOZ_ASSERT(thingSize >= sizeof(JSObject_Slots0));
+ static_assert(sizeof(JSObject_Slots0) >= CellSize,
"All allocations must be at least the allocator-imposed minimum size.");
if (!CheckAllocatorState<allowGC>(cx, kind))
return nullptr;
if (cx->isJSContext() &&
ShouldNurseryAllocateObject(cx->asJSContext()->nursery(), heap))
{
@@ -574,41 +574,43 @@ AllocateNonObject(ExclusiveContext *cx)
/*
* When allocating for initialization from a cached object copy, we will
* potentially destroy the cache entry we want to copy if we allow GC. On the
* other hand, since these allocations are extremely common, we don't want to
* delay GC from these allocation sites. Instead we allow the GC, but still
* fail the allocation, forcing the non-cached path.
*/
template <AllowGC allowGC>
-inline JSObject *
+inline NativeObject *
AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap, const js::Class *clasp)
{
+ MOZ_ASSERT(clasp->isNative());
+
if (ShouldNurseryAllocateObject(cx->nursery(), heap)) {
size_t thingSize = Arena::thingSize(kind);
MOZ_ASSERT(thingSize == Arena::thingSize(kind));
if (!CheckAllocatorState<NoGC>(cx, kind))
return nullptr;
JSObject *obj = TryNewNurseryObject<NoGC>(cx, thingSize, 0, clasp);
if (!obj && allowGC) {
cx->minorGC(JS::gcreason::OUT_OF_NURSERY);
return nullptr;
}
- return obj;
+ return reinterpret_cast<NativeObject *>(obj);
}
JSObject *obj = AllocateObject<NoGC>(cx, kind, 0, heap, clasp);
if (!obj && allowGC) {
cx->runtime()->gc.maybeGC(cx->zone());
return nullptr;
}
- return obj;
+ return reinterpret_cast<NativeObject *>(obj);
}
inline bool
IsInsideGGCNursery(const js::gc::Cell *cell)
{
if (!cell)
return false;
uintptr_t addr = uintptr_t(cell);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -595,17 +595,17 @@ VectorToKeyIterator(JSContext *cx, Handl
* computed for the cache lookup earlier, as constructing iterobj could
* have triggered a shape-regenerating GC. Don't bother with regenerating
* the shape key; if such a GC *does* occur, we can only get hits through
* the one-slot lastNativeIterator cache.
*/
JSObject *pobj = obj;
size_t ind = 0;
do {
- ni->shapes_array[ind++] = pobj->lastProperty();
+ ni->shapes_array[ind++] = pobj->as<NativeObject>().lastProperty();
pobj = pobj->getProto();
} while (pobj);
MOZ_ASSERT(ind == slength);
}
iterobj->setNativeIterator(ni);
objp.set(iterobj);
@@ -706,22 +706,22 @@ js::GetIterator(JSContext *cx, HandleObj
* will result in a miss.
*/
PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last;
if (last) {
NativeIterator *lastni = last->getNativeIterator();
if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
obj->isNative() &&
obj->as<NativeObject>().hasEmptyElements() &&
- obj->lastProperty() == lastni->shapes_array[0])
+ obj->as<NativeObject>().lastProperty() == lastni->shapes_array[0])
{
JSObject *proto = obj->getProto();
if (proto->isNative() &&
proto->as<NativeObject>().hasEmptyElements() &&
- proto->lastProperty() == lastni->shapes_array[1] &&
+ proto->as<NativeObject>().lastProperty() == lastni->shapes_array[1] &&
!proto->getProto())
{
objp.set(last);
UpdateNativeIterator(lastni, obj);
RegisterEnumerator(cx, last, lastni);
return true;
}
}
@@ -742,17 +742,17 @@ js::GetIterator(JSContext *cx, HandleObj
pobj->hasUncacheableProto() ||
pobj->getOps()->enumerate ||
pobj->getClass()->enumerate ||
pobj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic))
{
shapes.clear();
goto miss;
}
- Shape *shape = pobj->lastProperty();
+ Shape *shape = pobj->as<NativeObject>().lastProperty();
key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
if (!shapes.append(shape))
return false;
pobj = pobj->getProto();
} while (pobj);
}
PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2208,17 +2208,17 @@ js::CloneObjectLiteral(JSContext *cx, Ha
if (!group)
return nullptr;
RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, parent, kind,
MaybeSingletonObject));
if (!res)
return nullptr;
- RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->lastProperty(),
+ RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->as<PlainObject>().lastProperty(),
TaggedProto(proto), parent, kind));
if (!newShape || !res->setLastProperty(cx, newShape))
return nullptr;
return res;
}
RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
@@ -2289,17 +2289,17 @@ NativeObject::fillInAfterSwap(JSContext
}
void
JSObject::fixDictionaryShapeAfterSwap()
{
// Dictionary shapes can point back to their containing objects, so after
// swapping the guts of those objects fix the pointers up.
if (isNative() && as<NativeObject>().inDictionaryMode())
- shape_->listp = &shape_;
+ as<NativeObject>().shape_->listp = &as<NativeObject>().shape_;
}
/* Use this method with extreme caution. It trades the guts of two objects. */
bool
JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
{
// Ensure swap doesn't cause a finalizer to not be run.
MOZ_ASSERT(IsBackgroundFinalized(a->asTenured().getAllocKind()) ==
@@ -3655,17 +3655,17 @@ js::GetObjectSlotName(JSTracer *trc, cha
{
MOZ_ASSERT(trc->debugPrinter() == GetObjectSlotName);
JSObject *obj = (JSObject *)trc->debugPrintArg();
uint32_t slot = uint32_t(trc->debugPrintIndex());
Shape *shape;
if (obj->isNative()) {
- shape = obj->lastProperty();
+ shape = obj->as<NativeObject>().lastProperty();
while (shape && (!shape->hasSlot() || shape->slot() != slot))
shape = shape->previous();
} else {
shape = nullptr;
}
if (!shape) {
do {
@@ -3932,17 +3932,17 @@ JSObject::dump()
fprintf(stderr, "= ");
dumpValue(obj->as<NativeObject>().getSlot(i));
fputc('\n', stderr);
}
if (obj->isNative()) {
fprintf(stderr, "properties:\n");
Vector<Shape *, 8, SystemAllocPolicy> props;
- for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
+ for (Shape::Range<NoGC> r(obj->as<NativeObject>().lastProperty()); !r.empty(); r.popFront())
props.append(&r.front());
for (size_t i = props.length(); i-- != 0;)
DumpProperty(&obj->as<NativeObject>(), *props[i]);
}
fputc('\n', stderr);
}
static void
@@ -4112,24 +4112,25 @@ JSObject::addSizeOfExcludingThis(mozilla
}
}
void
JSObject::markChildren(JSTracer *trc)
{
MarkObjectGroup(trc, &group_, "group");
- MarkShape(trc, &shape_, "shape");
-
const Class *clasp = group_->clasp();
if (clasp->trace)
clasp->trace(trc, this);
- if (shape_->isNative()) {
+ if (clasp->isNative()) {
NativeObject *nobj = &as<NativeObject>();
+
+ MarkShape(trc, &nobj->shape_, "shape");
+
MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
do {
if (nobj->denseElementsAreCopyOnWrite()) {
HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
if (owner != nobj) {
MarkObject(trc, &owner, "objectElementsOwner");
break;
@@ -4138,8 +4139,21 @@ JSObject::markChildren(JSTracer *trc)
gc::MarkArraySlots(trc,
nobj->getDenseInitializedLength(),
nobj->getDenseElementsAllowCopyOnWrite(),
"objectElements");
} while (false);
}
}
+
+JSObject *
+JSObject::getParent() const
+{
+ if (Shape *shape = maybeShape())
+ return shape->getObjectParent();
+
+ // Avoid the parent-link checking in JSObject::global. Unboxed plain
+ // objects keep their compartment's global alive through their layout, and
+ // don't need a read barrier here.
+ MOZ_ASSERT(is<UnboxedPlainObject>());
+ return compartment()->unsafeUnbarrieredMaybeGlobal();
+}
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -87,52 +87,48 @@ class StrictArgumentsObject;
bool PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
bool SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded);
} /* namespace js */
/*
* A JavaScript object. The members common to all objects are as follows:
*
- * - The |shape_| member stores the shape of the object, which includes the
- * object's class and the layout of all its properties.
- *
* - The |group_| member stores the group of the object, which contains its
- * prototype object and the possible types of its properties.
+ * prototype object, its class and the possible types of its properties.
*
* Subclasses of JSObject --- mainly NativeObject and JSFunction --- add more
- * members.
+ * members. Notable among these is the object's shape, which stores flags and
+ * some other state, and, for native objects, the layout of all its properties.
+ * The second word of a JSObject generally stores its shape; if the second word
+ * stores anything else, the value stored cannot be a valid Shape* pointer, so
+ * that shape guards can be performed on objects without regard to the specific
+ * layout in use.
*/
class JSObject : public js::gc::Cell
{
protected:
js::HeapPtrObjectGroup group_;
- js::HeapPtrShape shape_;
private:
friend class js::Shape;
friend class js::GCMarker;
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::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj);
public:
- js::Shape * lastProperty() const {
- MOZ_ASSERT(shape_);
- return shape_;
- }
-
bool isNative() const {
- return lastProperty()->isNative();
+ return getClass()->isNative();
}
const js::Class *getClass() const {
return group_->clasp();
}
const JSClass *getJSClass() const {
return Jsvalify(getClass());
}
@@ -167,67 +163,70 @@ class JSObject : public js::gc::Cell
bool hasLazyGroup() const {
return group_->lazy();
}
JSCompartment *compartment() const {
return group_->compartment();
}
+ inline js::Shape *maybeShape() const;
+ inline js::Shape *ensureShape(js::ExclusiveContext *cx);
+
/*
* Make a non-array object with the specified initial state. This method
* takes ownership of any extantSlots it is passed.
*/
static inline JSObject *create(js::ExclusiveContext *cx,
js::gc::AllocKind kind,
js::gc::InitialHeap heap,
js::HandleShape shape,
js::HandleObjectGroup group);
+ // Set the shape of an object. This pointer is valid for native objects and
+ // some non-native objects. After creating an object, tobjects for which
+ // the shape pointer is invalid need to overwrite this pointer before a GC
+ // can occur.
+ inline void setInitialShapeMaybeNonNative(js::Shape *shape);
+ inline void setShapeMaybeNonNative(js::Shape *shape);
+
// Set the initial slots and elements of an object. These pointers are only
// valid for native objects, but during initialization are set for all
// objects. For non-native objects, these must not be dynamically allocated
// pointers which leak when the non-native object finishes initialization.
inline void setInitialSlotsMaybeNonNative(js::HeapSlot *slots);
inline void setInitialElementsMaybeNonNative(js::HeapSlot *elements);
enum GenerateShape {
GENERATE_NONE,
GENERATE_SHAPE
};
- bool setFlags(js::ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags,
+ bool setFlags(js::ExclusiveContext *cx, js::BaseShape::Flag flags,
GenerateShape generateShape = GENERATE_NONE);
+ inline bool hasAllFlags(js::BaseShape::Flag flags) const;
/*
* An object is a delegate if it is on another object's prototype or scope
* chain, and therefore the delegate might be asked implicitly to get or
* set a property on behalf of another object. Delegates may be accessed
* directly too, as may any object, but only those objects linked after the
* head of any prototype or scope chain are flagged as delegates. This
* definition helps to optimize shape-based property cache invalidation
* (see Purge{Scope,Proto}Chain in jsobj.cpp).
*/
- bool isDelegate() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::DELEGATE);
- }
-
+ inline bool isDelegate() const;
bool setDelegate(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
}
- bool isBoundFunction() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
- }
-
+ inline bool isBoundFunction() const;
inline bool hasSpecialEquality() const;
- bool watched() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
- }
+ inline bool watched() const;
bool setWatched(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
}
/* See InterpreterFrame::varObj. */
inline bool isQualifiedVarObj();
bool setQualifiedVarObj(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::QUALIFIED_VAROBJ);
@@ -239,49 +238,35 @@ class JSObject : public js::gc::Cell
}
/*
* Objects with an uncacheable proto can have their prototype mutated
* without inducing a shape change on the object. Property cache entries
* and JIT inline caches should not be filled for lookups across prototype
* lookups on the object.
*/
- bool hasUncacheableProto() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO);
- }
+ inline bool hasUncacheableProto() const;
bool setUncacheableProto(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
}
/*
* Whether SETLELEM was used to access this object. See also the comment near
* PropertyTree::MAX_HEIGHT.
*/
- bool hadElementsAccess() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::HAD_ELEMENTS_ACCESS);
- }
+ inline bool hadElementsAccess() const;
bool setHadElementsAccess(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
}
/*
* Whether there may be indexed properties on this object, excluding any in
* the object's elements.
*/
- bool isIndexed() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED);
- }
-
- uint32_t propertyCount() const {
- return lastProperty()->entryCount();
- }
-
- bool hasShapeTable() const {
- return lastProperty()->hasTable();
- }
+ inline bool isIndexed() const;
/* GC support. */
void markChildren(JSTracer *trc);
void fixupAfterMovingGC();
static js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
@@ -290,17 +275,17 @@ class JSObject : public js::gc::Cell
MOZ_ALWAYS_INLINE JS::Zone *zone() const {
return group_->zone();
}
MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZone() const {
return JS::shadow::Zone::asShadowZone(zone());
}
MOZ_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const {
- return shape_->zoneFromAnyThread();
+ return group_->zoneFromAnyThread();
}
MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
}
static MOZ_ALWAYS_INLINE void readBarrier(JSObject *obj);
static MOZ_ALWAYS_INLINE void writeBarrierPre(JSObject *obj);
static MOZ_ALWAYS_INLINE void writeBarrierPost(JSObject *obj, void *cellp);
static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject *obj, void *cellp);
@@ -372,48 +357,39 @@ class JSObject : public js::gc::Cell
bool hasLazyPrototype() const {
bool lazy = getTaggedProto().isLazy();
MOZ_ASSERT_IF(lazy, uninlinedIsProxy());
return lazy;
}
// 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);
- }
+ inline bool nonLazyPrototypeIsImmutable() const;
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);
- }
+ inline bool isIteratedSingleton() const;
bool setIteratedSingleton(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::ITERATED_SINGLETON);
}
/*
* Mark an object as requiring its default 'new' type to have unknown
* properties.
*/
- bool isNewGroupUnknown() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::NEW_GROUP_UNKNOWN);
- }
+ inline bool isNewGroupUnknown() const;
static bool setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
// Mark an object as having its 'new' script information cleared.
- bool wasNewScriptCleared() const {
- return lastProperty()->hasObjectFlag(js::BaseShape::NEW_SCRIPT_CLEARED);
- }
+ inline bool wasNewScriptCleared() const;
bool setNewScriptCleared(js::ExclusiveContext *cx) {
return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
}
/* Set a new prototype for an object with a singleton type. */
bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle<js::TaggedProto> proto);
/*
@@ -444,52 +420,43 @@ class JSObject : public js::gc::Cell
* In compileAndGo code, scope chains can contain only internal scope
* objects with a global object at the root as the scope of the outermost
* non-function script. In non-compileAndGo code, the scope of the
* outermost non-function script might not be a global object, and can have
* a mix of other objects above it before the global object is reached.
*/
/* Access the parent link of an object. */
- JSObject *getParent() const {
- return lastProperty()->getObjectParent();
- }
+ JSObject *getParent() const;
static bool setParent(JSContext *cx, js::HandleObject obj, js::HandleObject newParent);
/*
* Get the enclosing scope of an object. When called on non-scope object,
* this will just be the global (the name "enclosing scope" still applies
* in this situation because non-scope objects can be on the scope chain).
*/
inline JSObject *enclosingScope();
/* Access the metadata on an object. */
- inline JSObject *getMetadata() const {
- return lastProperty()->getObjectMetadata();
- }
+ inline JSObject *getMetadata() const;
static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata);
inline js::GlobalObject &global() const;
inline bool isOwnGlobal() const;
/*
* ES5 meta-object properties and operations.
*/
public:
// Indicates whether a non-proxy is extensible. Don't call on proxies!
// This method really shouldn't exist -- but there are a few internal
// places that want it (JITs and the like), and it'd be a pain to mark them
// all as friends.
- bool nonProxyIsExtensible() const {
- MOZ_ASSERT(!uninlinedIsProxy());
-
- // [[Extensible]] for ordinary non-proxy objects is an object flag.
- return !lastProperty()->hasObjectFlag(js::BaseShape::NOT_EXTENSIBLE);
- }
+ inline bool nonProxyIsExtensible() const;
public:
/*
* Iterator-specific getters and setters.
*/
static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
@@ -571,18 +538,18 @@ class JSObject : public js::gc::Cell
}
#ifdef DEBUG
void dump();
#endif
/* JIT Accessors */
- static size_t offsetOfShape() { return offsetof(JSObject, shape_); }
static size_t offsetOfGroup() { return offsetof(JSObject, group_); }
+ static size_t offsetOfShape() { return sizeof(JSObject); }
// Maximum size in bytes of a JSObject.
static const size_t MAX_BYTE_SIZE = 4 * sizeof(void *) + 16 * sizeof(JS::Value);
private:
JSObject() = delete;
JSObject(const JSObject &other) = delete;
void operator=(const JSObject &other) = delete;
@@ -619,22 +586,22 @@ operator==(const JSObject &lhs, const JS
static MOZ_ALWAYS_INLINE bool
operator!=(const JSObject &lhs, const JSObject &rhs)
{
return &lhs != &rhs;
}
// Size of the various GC thing allocation sizes used for objects.
-struct JSObject_Slots0 : JSObject { void *data[2]; };
-struct JSObject_Slots2 : JSObject { void *data[2]; js::Value fslots[2]; };
-struct JSObject_Slots4 : JSObject { void *data[2]; js::Value fslots[4]; };
-struct JSObject_Slots8 : JSObject { void *data[2]; js::Value fslots[8]; };
-struct JSObject_Slots12 : JSObject { void *data[2]; js::Value fslots[12]; };
-struct JSObject_Slots16 : JSObject { void *data[2]; js::Value fslots[16]; };
+struct JSObject_Slots0 : JSObject { void *data[3]; };
+struct JSObject_Slots2 : JSObject { void *data[3]; js::Value fslots[2]; };
+struct JSObject_Slots4 : JSObject { void *data[3]; js::Value fslots[4]; };
+struct JSObject_Slots8 : JSObject { void *data[3]; js::Value fslots[8]; };
+struct JSObject_Slots12 : JSObject { void *data[3]; js::Value fslots[12]; };
+struct JSObject_Slots16 : JSObject { void *data[3]; js::Value fslots[16]; };
/* static */ MOZ_ALWAYS_INLINE void
JSObject::readBarrier(JSObject *obj)
{
if (!isNullLike(obj) && obj->isTenured())
obj->asTenured().readBarrier(&obj->asTenured());
}
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -20,16 +20,34 @@
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
#include "jscompartmentinlines.h"
#include "jsgcinlines.h"
#include "vm/TypeInference-inl.h"
+inline js::Shape *
+JSObject::maybeShape() const
+{
+ if (is<js::UnboxedPlainObject>())
+ return nullptr;
+ return *reinterpret_cast<js::Shape **>(uintptr_t(this) + offsetOfShape());
+}
+
+inline js::Shape *
+JSObject::ensureShape(js::ExclusiveContext *cx)
+{
+ if (is<js::UnboxedPlainObject>() && !js::UnboxedPlainObject::convertToNative(cx->asJSContext(), this))
+ return nullptr;
+ js::Shape *shape = maybeShape();
+ MOZ_ASSERT(shape);
+ return shape;
+}
+
inline void
JSObject::finalize(js::FreeOp *fop)
{
js::probes::FinalizeObject(this);
#ifdef DEBUG
MOZ_ASSERT(isTenured());
if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
@@ -62,18 +80,18 @@ JSObject::finalize(js::FreeOp *fop)
fop->free_(elements);
}
}
// For dictionary objects (which must be native), it's possible that
// unreachable shapes may be marked whose listp points into this object.
// In case this happens, null out the shape's pointer here so that a moving
// GC will not try to access the dead object.
- if (shape_->listp == &shape_)
- shape_->listp = nullptr;
+ if (nobj->shape_->listp == &nobj->shape_)
+ nobj->shape_->listp = nullptr;
}
/* static */ inline bool
JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj)
{
MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj));
js::ObjectGroup *group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
@@ -183,25 +201,25 @@ js::DeleteElement(JSContext *cx, HandleO
/* * */
inline bool
JSObject::isQualifiedVarObj()
{
if (is<js::DebugScopeObject>())
return as<js::DebugScopeObject>().scope().isQualifiedVarObj();
- return lastProperty()->hasObjectFlag(js::BaseShape::QUALIFIED_VAROBJ);
+ return hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
}
inline bool
JSObject::isUnqualifiedVarObj()
{
if (is<js::DebugScopeObject>())
return as<js::DebugScopeObject>().scope().isUnqualifiedVarObj();
- return lastProperty()->hasObjectFlag(js::BaseShape::UNQUALIFIED_VAROBJ);
+ return hasAllFlags(js::BaseShape::UNQUALIFIED_VAROBJ);
}
namespace js {
inline bool
ClassCanHaveFixedData(const Class *clasp)
{
// Normally, the number of fixed slots given an object is the maximum
@@ -242,19 +260,20 @@ JSObject::create(js::ExclusiveContext *c
const js::Class *clasp = group->clasp();
size_t nDynamicSlots =
js::NativeObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap, clasp);
if (!obj)
return nullptr;
- obj->shape_.init(shape);
obj->group_.init(group);
+ obj->setInitialShapeMaybeNonNative(shape);
+
// Note: slots are created and assigned internally by NewGCObject.
obj->setInitialElementsMaybeNonNative(js::emptyObjectElements);
if (clasp->hasPrivate())
obj->as<js::NativeObject>().privateRef(shape->numFixedSlots()) = nullptr;
if (size_t span = shape->slotSpan())
obj->as<js::NativeObject>().initializeSlotRange(0, span);
@@ -264,27 +283,48 @@ JSObject::create(js::ExclusiveContext *c
memset(obj->as<JSFunction>().fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind));
js::gc::TraceCreateObject(obj);
return obj;
}
inline void
+JSObject::setInitialShapeMaybeNonNative(js::Shape *shape)
+{
+ static_cast<js::NativeObject *>(this)->shape_.init(shape);
+}
+
+inline void
+JSObject::setShapeMaybeNonNative(js::Shape *shape)
+{
+ MOZ_ASSERT(!is<js::UnboxedPlainObject>());
+ static_cast<js::NativeObject *>(this)->shape_ = shape;
+}
+
+inline void
JSObject::setInitialSlotsMaybeNonNative(js::HeapSlot *slots)
{
static_cast<js::NativeObject *>(this)->slots_ = slots;
}
inline void
JSObject::setInitialElementsMaybeNonNative(js::HeapSlot *elements)
{
static_cast<js::NativeObject *>(this)->elements_ = elements;
}
+inline JSObject *
+JSObject::getMetadata() const
+{
+ if (js::Shape *shape = maybeShape())
+ return shape->getObjectMetadata();
+ return nullptr;
+}
+
inline js::GlobalObject &
JSObject::global() const
{
#ifdef DEBUG
JSObject *obj = const_cast<JSObject *>(this);
while (JSObject *parent = obj->getParent())
obj = parent;
#endif
@@ -298,16 +338,95 @@ JSObject::global() const
}
inline bool
JSObject::isOwnGlobal() const
{
return &global() == this;
}
+inline bool
+JSObject::hasAllFlags(js::BaseShape::Flag flags) const
+{
+ MOZ_ASSERT(flags);
+ if (js::Shape *shape = maybeShape())
+ return shape->hasAllObjectFlags(flags);
+ return false;
+}
+
+inline bool
+JSObject::nonProxyIsExtensible() const
+{
+ MOZ_ASSERT(!uninlinedIsProxy());
+
+ // [[Extensible]] for ordinary non-proxy objects is an object flag.
+ return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
+}
+
+inline bool
+JSObject::isBoundFunction() const
+{
+ return hasAllFlags(js::BaseShape::BOUND_FUNCTION);
+}
+
+inline bool
+JSObject::watched() const
+{
+ return hasAllFlags(js::BaseShape::WATCHED);
+}
+
+inline bool
+JSObject::isDelegate() const
+{
+ return hasAllFlags(js::BaseShape::DELEGATE);
+}
+
+inline bool
+JSObject::hasUncacheableProto() const
+{
+ return hasAllFlags(js::BaseShape::UNCACHEABLE_PROTO);
+}
+
+inline bool
+JSObject::hadElementsAccess() const
+{
+ return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
+}
+
+inline bool
+JSObject::isIndexed() const
+{
+ return hasAllFlags(js::BaseShape::INDEXED);
+}
+
+inline bool
+JSObject::nonLazyPrototypeIsImmutable() const
+{
+ MOZ_ASSERT(!hasLazyPrototype());
+ return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE);
+}
+
+inline bool
+JSObject::isIteratedSingleton() const
+{
+ return hasAllFlags(js::BaseShape::ITERATED_SINGLETON);
+}
+
+inline bool
+JSObject::isNewGroupUnknown() const
+{
+ return hasAllFlags(js::BaseShape::NEW_GROUP_UNKNOWN);
+}
+
+inline bool
+JSObject::wasNewScriptCleared() const
+{
+ return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
+}
+
namespace js {
PropDesc::PropDesc(const Value &getter, const Value &setter,
Enumerability enumerable, Configurability configurable)
: value_(UndefinedValue()),
get_(getter), set_(setter),
attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
(enumerable ? JSPROP_ENUMERATE : 0) |
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -248,18 +248,18 @@ Shape::fixupDictionaryShapeAfterMovingGC
if (kind == FINALIZE_SHAPE || kind == FINALIZE_ACCESSOR_SHAPE) {
// listp points to the parent field of the next shape.
Shape *next = reinterpret_cast<Shape *>(uintptr_t(listp) -
offsetof(Shape, parent));
listp = &gc::MaybeForwarded(next)->parent;
} else {
// listp points to the shape_ field of an object.
JSObject *last = reinterpret_cast<JSObject *>(uintptr_t(listp) -
- offsetof(JSObject, shape_));
- listp = &gc::MaybeForwarded(last)->shape_;
+ JSObject::offsetOfShape());
+ listp = &gc::MaybeForwarded(last)->as<NativeObject>().shape_;
}
}
void
Shape::fixupShapeTreeAfterMovingGC()
{
if (kids.isNull())
return;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -606,16 +606,18 @@ js::proxy_Trace(JSTracer *trc, JSObject
ProxyObject::trace(trc, obj);
}
/* static */ void
ProxyObject::trace(JSTracer *trc, JSObject *obj)
{
ProxyObject *proxy = &obj->as<ProxyObject>();
+ MarkShape(trc, &proxy->shape, "ProxyObject_shape");
+
#ifdef DEBUG
if (trc->runtime()->gc.isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
JSObject *referent = MaybeForwarded(&proxy->private_().toObject());
if (referent->compartment() != proxy->compartment()) {
/*
* Assert that this proxy is tracked in the wrapper map. We maintain
* the invariant that the wrapped object is the key in the wrapper map.
*/
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2879,17 +2879,17 @@ static bool
ShapeOf(JSContext *cx, unsigned argc, JS::Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.get(0).isObject()) {
JS_ReportError(cx, "shapeOf: object expected");
return false;
}
JSObject *obj = &args[0].toObject();
- args.rval().set(JS_NumberValue(double(uintptr_t(obj->lastProperty()) >> 3)));
+ args.rval().set(JS_NumberValue(double(uintptr_t(obj->maybeShape()) >> 3)));
return true;
}
/*
* Check that t1 comes strictly before t2. The function correctly deals with
* wrap-around between t2 and t1 assuming that t2 and t1 stays within INT32_MAX
* from each other. We use MAX_TIMEOUT_INTERVAL to enforce this restriction.
*/
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -381,23 +381,24 @@ NativeObject::setLastPropertyMakeNonNati
shape_ = shape;
}
void
NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, Shape *shape)
{
MOZ_ASSERT(getClass()->isNative());
- MOZ_ASSERT(!lastProperty()->isNative());
MOZ_ASSERT(shape->isNative());
- MOZ_ASSERT(!inDictionaryMode());
MOZ_ASSERT(!shape->inDictionary());
- MOZ_ASSERT(shape->compartment() == compartment());
- shape_ = shape;
+ // This method is used to convert unboxed objects into native objects. In
+ // this case, the shape_ field was previously used to store other data and
+ // this should be treated as an initialization.
+ shape_.init(shape);
+
slots_ = nullptr;
elements_ = emptyObjectElements;
size_t oldSpan = shape->numFixedSlots();
size_t newSpan = shape->slotSpan();
// A failure at this point will leave the object as a mutant, and we
// can't recover.
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -336,16 +336,19 @@ DenseRangeWriteBarrierPost(JSRuntime *rt
* in this case numFixedSlots() is zero) or to a dynamically allocated array.
*
* Slots and elements may both be non-empty. The slots may be either names or
* indexes; no indexed property will be in both the slots and elements.
*/
class NativeObject : public JSObject
{
protected:
+ // Property layout description and other state.
+ HeapPtrShape shape_;
+
/* Slots for object properties. */
js::HeapSlot *slots_;
/* Slots for object dense elements. */
js::HeapSlot *elements_;
friend bool
ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs,
@@ -373,16 +376,29 @@ class NativeObject : public JSObject
static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
"verify numFixedSlots() bitfield is big enough");
static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
"inconsistent maximum object size");
}
public:
+ Shape *lastProperty() const {
+ MOZ_ASSERT(shape_);
+ return shape_;
+ }
+
+ uint32_t propertyCount() const {
+ return lastProperty()->entryCount();
+ }
+
+ bool hasShapeTable() const {
+ return lastProperty()->hasTable();
+ }
+
HeapSlotArray getDenseElements() {
return HeapSlotArray(elements_, !getElementsHeader()->isCopyOnWrite());
}
HeapSlotArray getDenseElementsAllowCopyOnWrite() {
// Backdoor allowing direct access to copy on write elements.
return HeapSlotArray(elements_, true);
}
const Value &getDenseElement(uint32_t idx) {
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -307,17 +307,17 @@ JSObject::makeLazyGroup(JSContext *cx, H
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_SINGLETON | OBJECT_FLAG_NON_PACKED;
- if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
+ if (obj->isIteratedSingleton())
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;
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -165,17 +165,17 @@ js::ForOfPIC::Stub *
js::ForOfPIC::Chain::getMatchingStub(JSObject *obj)
{
// Ensure PIC is initialized and not disabled.
if (!initialized_ || disabled_)
return nullptr;
// Check if there is a matching stub.
for (Stub *stub = stubs(); stub != nullptr; stub = stub->next()) {
- if (stub->shape() == obj->lastProperty())
+ if (stub->shape() == obj->maybeShape())
return stub;
}
return nullptr;
}
bool
js::ForOfPIC::Chain::isOptimizableArray(JSObject *obj)
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -11,16 +11,18 @@
#include "vm/NativeObject.h"
namespace js {
// This is the base class for the various kinds of proxy objects. It's never
// instantiated.
class ProxyObject : public JSObject
{
+ HeapPtrShape shape;
+
// GetProxyDataLayout computes the address of this field.
ProxyDataLayout data;
void static_asserts() {
static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0),
"proxy object size must match GC thing size");
static_assert(offsetof(ProxyObject, data) == ProxyDataOffset,
"proxy object layout must match shadow interface");
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -33,38 +33,38 @@ NewObjectCache::lookupGlobal(const Class
inline void
NewObjectCache::fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global,
gc::AllocKind kind, NativeObject *obj)
{
//MOZ_ASSERT(global == obj->getGlobal());
return fill(entry, clasp, global, kind, obj);
}
-inline JSObject *
+inline NativeObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, js::gc::InitialHeap heap)
{
// The new object cache does not account for metadata attached via callbacks.
MOZ_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
MOZ_ASSERT(unsigned(entryIndex) < mozilla::ArrayLength(entries));
Entry *entry = &entries[entryIndex];
- JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
+ NativeObject *templateObj = reinterpret_cast<NativeObject *>(&entry->templateObject);
// Do an end run around JSObject::group() to avoid doing AutoUnprotectCell
// on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread.
ObjectGroup *group = templateObj->group_;
if (group->shouldPreTenure())
heap = gc::TenuredHeap;
if (cx->runtime()->gc.upcomingZealousGC())
return nullptr;
- JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp());
+ NativeObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp());
if (obj) {
copyCachedToObject(obj, templateObj, entry->kind);
probes::CreateObject(cx, obj);
js::gc::TraceCreateObject(obj);
return obj;
}
// Trigger an identical allocation to the one that notified us of OOM so
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -292,17 +292,17 @@ class NewObjectCache
return lookup(group->clasp(), group, kind, pentry);
}
/*
* Return a new object from a cache hit produced by a lookup method, or
* nullptr if returning the object could possibly trigger GC (does not
* indicate failure).
*/
- inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
+ inline NativeObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
/* Fill an entry after a cache miss. */
void fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
gc::AllocKind kind, NativeObject *obj);
inline void fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global,
gc::AllocKind kind, NativeObject *obj);
@@ -339,17 +339,17 @@ class NewObjectCache
entry->clasp = clasp;
entry->key = key;
entry->kind = kind;
entry->nbytes = gc::Arena::thingSize(kind);
js_memcpy(&entry->templateObject, obj, entry->nbytes);
}
- static void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind) {
+ static void copyCachedToObject(NativeObject *dst, NativeObject *src, gc::AllocKind kind) {
js_memcpy(dst, src, gc::Arena::thingSize(kind));
Shape::writeBarrierPost(dst->shape_, &dst->shape_);
ObjectGroup::writeBarrierPost(dst->group_, &dst->group_);
}
};
/*
* A FreeOp can do one thing: free memory. For convenience, it has delete_
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -494,17 +494,17 @@ NativeObject::addProperty(ExclusiveConte
if (obj->inDictionaryMode())
entry = &obj->lastProperty()->table().search(id, true);
return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, entry,
allowDictionary);
}
static bool
-ShouldConvertToDictionary(JSObject *obj)
+ShouldConvertToDictionary(NativeObject *obj)
{
/*
* Use a lower limit if this object is likely a hashmap (SETELEM was used
* to set properties).
*/
if (obj->hadElementsAccess())
return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT_WITH_ELEMENTS_ACCESS;
return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT;
@@ -1133,31 +1133,35 @@ NativeObject::shadowingShapeChange(Exclu
/* static */ bool
JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent)
{
if (parent && !parent->setDelegate(cx))
return false;
if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
- StackBaseShape base(obj->lastProperty());
+ StackBaseShape base(obj->as<NativeObject>().lastProperty());
base.parent = parent;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
- obj->lastProperty()->base()->adoptUnowned(nbase);
+ obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
return true;
}
- Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_);
+ Shape *existingShape = obj->ensureShape(cx);
+ if (!existingShape)
+ return false;
+
+ Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), existingShape);
if (!newShape)
return false;
- obj->shape_ = newShape;
+ obj->setShapeMaybeNonNative(newShape);
return true;
}
/* static */ Shape *
Shape::setObjectParent(ExclusiveContext *cx, JSObject *parent, TaggedProto proto, Shape *last)
{
if (last->getObjectParent() == parent)
return last;
@@ -1168,31 +1172,35 @@ Shape::setObjectParent(ExclusiveContext
RootedShape lastRoot(cx, last);
return replaceLastProperty(cx, base, proto, lastRoot);
}
/* static */ bool
JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata)
{
if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
- StackBaseShape base(obj->lastProperty());
+ StackBaseShape base(obj->as<NativeObject>().lastProperty());
base.metadata = metadata;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
- obj->lastProperty()->base()->adoptUnowned(nbase);
+ obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
return true;
}
- Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), obj->shape_);
+ Shape *existingShape = obj->ensureShape(cx);
+ if (!existingShape)
+ return false;
+
+ Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), existingShape);
if (!newShape)
return false;
- obj->shape_ = newShape;
+ obj->setShapeMaybeNonNative(newShape);
return true;
}
/* static */ Shape *
Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last)
{
if (last->getObjectMetadata() == metadata)
return last;
@@ -1200,55 +1208,55 @@ Shape::setObjectMetadata(JSContext *cx,
StackBaseShape base(last);
base.metadata = metadata;
RootedShape lastRoot(cx, last);
return replaceLastProperty(cx, base, proto, lastRoot);
}
bool
-JSObject::setFlags(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags_,
- GenerateShape generateShape)
+JSObject::setFlags(ExclusiveContext *cx, BaseShape::Flag flags, GenerateShape generateShape)
{
- BaseShape::Flag flags = (BaseShape::Flag) flags_;
-
- if ((lastProperty()->getObjectFlags() & flags) == flags)
+ if (hasAllFlags(flags))
return true;
RootedObject self(cx, this);
if (isNative() && as<NativeObject>().inDictionaryMode()) {
if (generateShape == GENERATE_SHAPE && !as<NativeObject>().generateOwnShape(cx))
return false;
- StackBaseShape base(self->lastProperty());
+ StackBaseShape base(self->as<NativeObject>().lastProperty());
base.flags |= flags;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
- self->lastProperty()->base()->adoptUnowned(nbase);
+ self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
return true;
}
- Shape *newShape =
- Shape::setObjectFlags(cx, flags, self->getTaggedProto(), self->lastProperty());
+ Shape *existingShape = self->ensureShape(cx);
+ if (!existingShape)
+ return false;
+
+ Shape *newShape = Shape::setObjectFlags(cx, flags, self->getTaggedProto(), existingShape);
if (!newShape)
return false;
- self->shape_ = newShape;
+ self->setShapeMaybeNonNative(newShape);
return true;
}
bool
NativeObject::clearFlag(ExclusiveContext *cx, BaseShape::Flag flag)
{
MOZ_ASSERT(inDictionaryMode());
- MOZ_ASSERT(lastProperty()->getObjectFlags() & flag);
- RootedObject self(cx, this);
+ RootedNativeObject self(cx, &as<NativeObject>());
+ MOZ_ASSERT(self->lastProperty()->getObjectFlags() & flag);
StackBaseShape base(self->lastProperty());
base.flags &= ~flag;
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
self->lastProperty()->base()->adoptUnowned(nbase);
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -829,19 +829,20 @@ class Shape : public gc::TenuredCell
static Shape *setObjectParent(ExclusiveContext *cx,
JSObject *obj, TaggedProto proto, Shape *last);
static Shape *setObjectMetadata(JSContext *cx,
JSObject *metadata, TaggedProto proto, Shape *last);
static Shape *setObjectFlags(ExclusiveContext *cx,
BaseShape::Flag flag, TaggedProto proto, Shape *last);
uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
- bool hasObjectFlag(BaseShape::Flag flag) const {
- MOZ_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
- return !!(base()->flags & flag);
+ bool hasAllObjectFlags(BaseShape::Flag flags) const {
+ MOZ_ASSERT(flags);
+ MOZ_ASSERT(!(flags & ~BaseShape::OBJECT_FLAG_MASK));
+ return (base()->flags & flags) == flags;
}
protected:
/*
* Implementation-private bits stored in shape->flags. See public: enum {}
* flags further below, which were allocated FCFS over time, so interleave
* with these bits.
*/
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2571,17 +2571,17 @@ ObjectGroup::matchDefiniteProperties(Han
for (unsigned i = 0; i < count; i++) {
Property *prop = getProperty(i);
if (!prop)
continue;
if (prop->types.definiteProperty()) {
unsigned slot = prop->types.definiteSlot();
bool found = false;
- Shape *shape = obj->lastProperty();
+ Shape *shape = obj->as<NativeObject>().lastProperty();
while (!shape->isEmptyShape()) {
if (shape->slot() == slot && shape->propid() == prop->id) {
found = true;
break;
}
shape = shape->previous();
}
if (!found)
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -283,54 +283,40 @@ UnboxedPlainObject::convertToNative(JSCo
}
AutoValueVector values(cx);
for (size_t i = 0; i < layout.properties().length(); i++) {
if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i])))
return false;
}
- uint32_t objectFlags = obj->lastProperty()->getObjectFlags();
- RootedObject metadata(cx, obj->getMetadata());
-
obj->setGroup(layout.nativeGroup());
obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
for (size_t i = 0; i < values.length(); i++)
obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
- if (objectFlags) {
- RootedObject objRoot(cx, obj);
- if (!obj->setFlags(cx, objectFlags))
- return false;
- obj = objRoot;
- }
-
- if (metadata) {
- RootedObject objRoot(cx, obj);
- RootedObject metadataRoot(cx, metadata);
- if (!setMetadata(cx, objRoot, metadataRoot))
- return false;
- }
-
return true;
}
/* static */
UnboxedPlainObject *
UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind)
{
MOZ_ASSERT(group->clasp() == &class_);
gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
UnboxedPlainObject *res = NewObjectWithGroup<UnboxedPlainObject>(cx, group, cx->global(),
allocKind, newKind);
if (!res)
return nullptr;
+ // Avoid spurious shape guard hits.
+ res->dummy_ = nullptr;
+
// Initialize reference fields of the object. All fields in the object will
// be overwritten shortly, but references need to be safe for the GC.
const int32_t *list = res->layout().traceList();
if (list) {
uint8_t *data = res->data();
while (*list != -1) {
HeapPtrString *heap = reinterpret_cast<HeapPtrString *>(data + *list);
heap->init(cx->names().empty);
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -146,17 +146,20 @@ class UnboxedLayout : public mozilla::Li
};
// Class for a plain object using an unboxed representation. The physical
// layout of these objects is identical to that of an InlineTypedObject, though
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
// how their properties are stored.
class UnboxedPlainObject : public JSObject
{
- // Start of the inline data, which immediately follows the shape and type.
+ // Placeholder for extra properties. See bug 1137180.
+ void *dummy_;
+
+ // Start of the inline data, which immediately follows the group and extra properties.
uint8_t data_[1];
public:
static const Class class_;
static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleObject objp,
MutableHandleShape propp);