author | Brian Hackett <bhackett1024@gmail.com> |
Wed, 04 Mar 2015 08:32:45 -0600 | |
changeset 231873 | afda1ff329bf2d1da22e0f09eb1c24537eefb7f4 |
parent 231872 | 0709fe191f0072fcaec3daadbe3f30d016433b1f |
child 231874 | aaa85558f413b82ccf321a5a738c05c5c2538b64 |
push id | 28362 |
push user | ryanvm@gmail.com |
push date | Wed, 04 Mar 2015 21:35:51 +0000 |
treeherder | mozilla-central@56492f7244a9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1137497 |
milestone | 39.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/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);