☠☠ backed out by 29f94bde2afa ☠ ☠ | |
author | Jeff Walden <jwalden@mit.edu> |
Fri, 18 Mar 2016 16:45:31 -0700 | |
changeset 295160 | 3c4b7e1de6290ef6e21f2f9e17f99ee5a04f47c6 |
parent 295159 | fc1b655d1080f56248817d4ecb1c5446376f6c19 |
child 295161 | b805df0c8296d6bf85d88e2bd702a790b3bf676d |
push id | 75833 |
push user | jwalden@mit.edu |
push date | Thu, 28 Apr 2016 05:48:21 +0000 |
treeherder | mozilla-inbound@3c4b7e1de629 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | efaust |
bugs | 1263778 |
milestone | 49.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/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -32,19 +32,18 @@ public: JS::ObjectOpResult &result) const override; virtual bool ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags, JS::AutoIdVector& aProps) const override; virtual bool delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, JS::ObjectOpResult &aResult) const override; - // No need for getPrototypeIfOrdinary here: this object shouldn't have a - // lazy prototype, so this trap would never be called (and the inherited - // version, from BaseProxyHandler, just crashes). + // No need for getPrototypeIfOrdinary here: window named-properties objects + // have static prototypes, so this trap is never called. virtual bool preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::ObjectOpResult& aResult) const override { return aResult.failCantPreventExtensions(); } virtual bool
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -919,23 +919,24 @@ nsOuterWindowProxy::getPrototypeIfOrdina JS::Handle<JSObject*> proxy, bool* isOrdinary, JS::MutableHandle<JSObject*> protop) const { // Window's [[GetPrototypeOf]] trap isn't the ordinary definition: // // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof // - // We nonetheless can implement it here using a non-"lazy" [[Prototype]], - // because wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) - // supply all the non-ordinary behavior. + // We nonetheless can implement it with a static [[Prototype]], because + // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply + // all non-ordinary behavior. // // But from a spec point of view, it's the exact same object in both cases -- - // only the observer's changed. So both cases *must* report non-ordinary, - // even if non-"lazy" [[Prototype]] usually means ordinary. + // only the observer's changed. So this getPrototypeIfOrdinary trap on the + // non-wrapper object *must* report non-ordinary, even if static [[Prototype]] + // usually means ordinary. *isOrdinary = false; return true; } bool nsOuterWindowProxy::preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy, JS::ObjectOpResult& result) const
--- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -227,17 +227,17 @@ MapIteratorObject::next(JSContext* cx, H /* static */ JSObject* MapIteratorObject::createResultPair(JSContext* cx) { RootedArrayObject resultPairObj(cx, NewDenseFullyAllocatedArray(cx, 2, nullptr, TenuredObject)); if (!resultPairObj) return nullptr; - Rooted<TaggedProto> proto(cx, resultPairObj->getTaggedProto()); + Rooted<TaggedProto> proto(cx, resultPairObj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto); if (!group) return nullptr; resultPairObj->setGroup(group); resultPairObj->setDenseInitializedLength(2); resultPairObj->initDenseElement(0, NullValue()); resultPairObj->initDenseElement(1, NullValue());
--- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -606,17 +606,17 @@ js::ObjectCreateImpl(JSContext* cx, Hand } return NewObjectWithGivenProto<PlainObject>(cx, proto, allocKind, newKind); } PlainObject* js::ObjectCreateWithTemplate(JSContext* cx, HandlePlainObject templateObj) { - RootedObject proto(cx, templateObj->getProto()); + RootedObject proto(cx, templateObj->staticPrototype()); RootedObjectGroup group(cx, templateObj->group()); return ObjectCreateImpl(cx, proto, GenericObject, group); } // ES6 draft rev34 (2015/02/20) 19.1.2.2 Object.create(O [, Properties]) bool js::obj_create(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -1604,22 +1604,22 @@ js::RegExpInstanceOptimizableRaw(JSConte NativeObject* nobj = static_cast<NativeObject*>(rx); Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape(); if (shape == nobj->lastProperty()) { *result = true; return true; } - if (rx->hasLazyPrototype()) { + if (!rx->hasStaticPrototype()) { *result = false; return true; } - if (rx->getTaggedProto().toObjectOrNull() != proto) { + if (rx->staticPrototype() != proto) { *result = false; return true; } if (!RegExpObject::isInitialShape(nobj)) { *result = false; return true; }
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1677,17 +1677,17 @@ TypedObject::obj_lookupProperty(JSContex MutableHandleObject objp, MutableHandleShape propp) { if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) { MarkNonNativePropertyFound<CanGC>(propp); objp.set(obj); return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); return true; } return LookupProperty(cx, proto, id, objp, propp); } @@ -1749,17 +1749,17 @@ TypedObject::obj_hasProperty(JSContext* case type::Struct: size_t index; if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) { *foundp = true; return true; } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; } return HasProperty(cx, proto, id, foundp); } @@ -1806,17 +1806,17 @@ TypedObject::obj_getProperty(JSContext* break; size_t offset = descr->fieldOffset(fieldIndex); Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex)); return Reify(cx, fieldType, typedObj, offset, vp); } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; } return GetProperty(cx, proto, receiver, id, vp); } @@ -1834,17 +1834,17 @@ TypedObject::obj_getElement(JSContext* c case type::Simd: case type::Struct: break; case type::Array: return obj_getArrayElement(cx, typedObj, descr, index, vp); } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; } return GetElement(cx, proto, receiver, index, vp); } @@ -2017,17 +2017,17 @@ IsOwnId(JSContext* cx, HandleObject obj, } bool TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result) { if (IsOwnId(cx, obj, id)) return ReportPropertyError(cx, JSMSG_CANT_DELETE, id); - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) return result.succeed(); return DeleteProperty(cx, proto, id, result); } bool TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -525,17 +525,18 @@ class TypedObject : public JSObject static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result); static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, bool enumerableOnly); public: TypedProto& typedProto() const { - return getProto()->as<TypedProto>(); + // Typed objects' prototypes can't be modified. + return staticPrototype()->as<TypedProto>(); } TypeDescr& typeDescr() const { return group()->typeDescr(); } int32_t offset() const; int32_t length() const;
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -780,22 +780,24 @@ IsCacheableSetPropAddSlot(JSContext* cx, obj->getClass()->getAddProperty()) { return false; } size_t chainDepth = 0; // Walk up the object prototype chain and ensure that all prototypes are // native, and that all prototypes have no setter defined on the property. - for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) { + for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) { chainDepth++; // if prototype is non-native, don't optimize if (!proto->isNative()) return false; + MOZ_ASSERT(proto->hasStaticPrototype()); + // if prototype defines this property in a non-plain way, don't optimize Shape* protoShape = proto->as<NativeObject>().lookup(cx, id); if (protoShape && !protoShape->hasDefaultSetter()) return false; // Otherwise, if there's no such property, watch out for a resolve hook // that would need to be invoked and thus prevent inlining of property // addition. @@ -2347,23 +2349,23 @@ static bool SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj) { static const size_t MAX_DEPTH = ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH; ICSetElem_DenseOrUnboxedArrayAddImpl<MAX_DEPTH>* nstub = stub->toImplUnchecked<MAX_DEPTH>(); if (obj->maybeShape() != nstub->shape(0)) return false; - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); for (size_t i = 0; i < stub->protoChainDepth(); i++) { if (!proto->isNative()) return false; if (proto->as<NativeObject>().lastProperty() != nstub->shape(i + 1)) return false; - proto = obj->getProto(); + proto = obj->staticPrototype(); if (!proto) { if (i != stub->protoChainDepth() - 1) return false; break; } } return true; @@ -2474,22 +2476,22 @@ CanOptimizeDenseOrUnboxedArraySetElem(JS return false; // The checks are not complete. The object may have a setter definition, // either directly, or via a prototype, or via the target object for a prototype // which is a proxy, that handles a particular integer write. // Scan the prototype and shape chain to make sure that this is not the case. if (obj->isIndexed()) return false; - JSObject* curObj = obj->getProto(); + JSObject* curObj = obj->staticPrototype(); while (curObj) { ++*protoDepthOut; if (!curObj->isNative() || curObj->isIndexed()) return false; - curObj = curObj->getProto(); + curObj = curObj->staticPrototype(); } if (*protoDepthOut > ICSetElem_DenseOrUnboxedArrayAdd::MAX_PROTO_CHAIN_DEPTH) return false; *isAddingCaseOut = true; return true; } @@ -3693,17 +3695,17 @@ TryAttachGlobalNameValueStub(JSContext* RootedNativeObject current(cx, globalLexical); while (true) { shape = current->lookup(cx, id); if (shape) break; if (current == globalLexical) { current = &globalLexical->global(); } else { - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is<NativeObject>()) return true; current = &proto->as<NativeObject>(); } } // Instantiate this global property, for use during Ion compilation. if (IsIonEnabled(cx)) @@ -3768,17 +3770,17 @@ TryAttachGlobalNameAccessorStub(JSContex // The property must be found, and it must be found as a normal data property. RootedShape shape(cx); RootedNativeObject current(cx, global); while (true) { shape = current->lookup(cx, id); if (shape) break; - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is<NativeObject>()) return true; current = &proto->as<NativeObject>(); } // Instantiate this global property, for use during Ion compilation. if (IsIonEnabled(cx)) EnsureTrackPropertyTypes(cx, current, id);
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -128,36 +128,38 @@ GeneratePrototypeGuards(CacheIRWriter& w // prototype chain is directly altered, then TI will toss the jitcode, so we // don't have to worry about it, and any other change to the holder, or // adding a shadowing property will result in reshaping the holder, and thus // the failure of the shape guard. MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // If the shape does not imply the proto, emit an explicit proto guard. - writer.guardProto(objId, obj->getProto()); + writer.guardProto(objId, obj->staticPrototype()); } - JSObject* pobj = IsCacheableDOMProxy(obj) - ? obj->getTaggedProto().toObjectOrNull() - : obj->getProto(); + MOZ_ASSERT(obj->isNative() != IsCacheableDOMProxy(obj), + "only regular objects, or DOM proxies with static prototype, " + "should be observed here"); + + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { ObjOperandId protoId = writer.loadObject(pobj); if (pobj->isSingleton()) { // Singletons can have their group's |proto| mutated directly. - writer.guardProto(protoId, pobj->getProto()); + writer.guardProto(protoId, pobj->staticPrototype()); } else { writer.guardGroup(protoId, pobj->group()); } } - pobj = pobj->getProto(); + pobj = pobj->staticPrototype(); } } static void TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId) { if (obj->is<UnboxedPlainObject>()) { writer.guardGroup(objId, obj->group()); @@ -190,22 +192,22 @@ EmitReadSlotResult(CacheIRWriter& writer if (holder) { // Guard on the holder's shape. holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->as<NativeObject>().lastProperty()); } else { // The property does not exist. Guard on everything in the prototype // chain. This is guaranteed to see only Native objects because of // CanAttachNativeGetProp(). - JSObject* proto = obj->getTaggedProto().toObjectOrNull(); + JSObject* proto = obj->taggedProto().toObjectOrNull(); ObjOperandId lastObjId = objId; while (proto) { ObjOperandId protoId = writer.loadProto(lastObjId); writer.guardShape(protoId, proto->as<NativeObject>().lastProperty()); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastObjId = protoId; } } } else if (obj->is<UnboxedPlainObject>()) { holder = obj->as<UnboxedPlainObject>().maybeExpando(); holderId = writer.loadUnboxedExpando(objId); } else { holderId = objId;
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6266,17 +6266,17 @@ IonBuilder::createThisScriptedSingleton( if (!proto) return nullptr; JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; if (!templateObject->is<PlainObject>() && !templateObject->is<UnboxedPlainObject>()) return nullptr; - if (templateObject->getProto() != proto) + if (templateObject->staticPrototype() != proto) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED)) return nullptr; StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript()); if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject))) @@ -6313,17 +6313,17 @@ IonBuilder::createThisScriptedBaseline(M if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) return nullptr; Value protov = target->getSlot(shape->slot()); if (!protov.isObject()) return nullptr; JSObject* proto = checkNurseryObject(&protov.toObject()); - if (proto != templateObject->getProto()) + if (proto != templateObject->staticPrototype()) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED)) return nullptr; StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript()); if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject))) @@ -8101,17 +8101,17 @@ IonBuilder::testSingletonProperty(JSObje if (obj->isSingleton()) return property.singleton(constraints()); return nullptr; } if (ObjectHasExtraOwnProperty(compartment, objKey, id)) return nullptr; - obj = checkNurseryObject(obj->getProto()); + obj = checkNurseryObject(obj->staticPrototype()); } return nullptr; } JSObject* IonBuilder::testSingletonPropertyTypes(MDefinition* obj, jsid id) {
--- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -417,54 +417,58 @@ GeneratePrototypeGuards(JSContext* cx, I */ MOZ_ASSERT(obj != holder); if (obj->hasUncacheableProto()) { // Note: objectReg and scratchReg may be the same register, so we cannot // use objectReg in the rest of this function. masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg); Address proto(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), failures); } - JSObject* pobj = IsCacheableDOMProxy(obj) - ? obj->getTaggedProto().toObjectOrNull() - : obj->getProto(); + MOZ_ASSERT(obj->isNative() != IsCacheableDOMProxy(obj), + "only regular objects, or DOM proxies with static prototype, " + "should be observed here"); + + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; while (pobj != holder) { if (pobj->hasUncacheableProto()) { masm.movePtr(ImmGCPtr(pobj), scratchReg); Address groupAddr(scratchReg, JSObject::offsetOfGroup()); if (pobj->isSingleton()) { // Singletons can have their group's |proto| mutated directly. masm.loadPtr(groupAddr, scratchReg); Address protoAddr(scratchReg, ObjectGroup::offsetOfProto()); - masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->getProto()), failures); + masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->staticPrototype()), + failures); } else { masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures); } } - pobj = pobj->getProto(); + + pobj = pobj->staticPrototype(); } } // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that // Ion caches can deal with objects on the proto chain that have uncacheable // prototypes. bool jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder) { while (obj != holder) { /* * We cannot assume that we find the holder object on the prototype * chain and must check for null proto. The prototype chain can be * altered during the lookupProperty call. */ - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto || !proto->isNative()) return false; obj = proto; } return true; } bool @@ -495,17 +499,17 @@ IsCacheableNoProperty(JSObject* obj, JSO // Don't generate missing property ICs if we skipped a non-native object, as // lookups may extend beyond the prototype chain (e.g. for DOMProxy // proxies). JSObject* obj2 = obj; while (obj2) { if (!obj2->isNative()) return false; - obj2 = obj2->getProto(); + obj2 = obj2->staticPrototype(); } // The pc is nullptr if the cache is idempotent. We cannot share missing // properties between caches because TI can only try to prove that a type is // contained, but does not attempts to check if something does not exists. // So the infered type of getprop would be missing and would not contain // undefined, as expected for missing properties. if (!pc) @@ -814,29 +818,30 @@ GenerateReadSlot(JSContext* cx, IonScrip masm.movePtr(ImmGCPtr(holder), holderReg); masm.branchPtr(Assembler::NotEqual, Address(holderReg, JSObject::offsetOfShape()), ImmGCPtr(holder->as<NativeObject>().lastProperty()), &prototypeFailures); } else { // The property does not exist. Guard on everything in the // prototype chain. - JSObject* proto = obj->getTaggedProto().toObjectOrNull(); + JSObject* proto = obj->staticPrototype(); Register lastReg = object; MOZ_ASSERT(scratchReg != object); while (proto) { masm.loadObjProto(lastReg, scratchReg); // Guard the shape of the current prototype. + MOZ_ASSERT(proto->hasStaticPrototype()); masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()), ImmGCPtr(proto->as<NativeObject>().lastProperty()), &prototypeFailures); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastReg = scratchReg; } holderReg = InvalidReg; } } else if (obj->is<UnboxedPlainObject>()) { holder = obj->as<UnboxedPlainObject>().maybeExpando(); holderReg = scratchReg; @@ -1798,17 +1803,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow void* returnAddr, bool* emitted) { MOZ_ASSERT(canAttachStub()); MOZ_ASSERT(!*emitted); MOZ_ASSERT(IsCacheableDOMProxy(obj)); MOZ_ASSERT(monitoredResult()); MOZ_ASSERT(output().hasValue()); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); RootedNativeObject holder(cx); RootedShape shape(cx); NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx, *this, checkObj, id, &holder, &shape, /* skipArrayLen = */true); MOZ_ASSERT(canCache != CanAttachArrayLength); @@ -2901,17 +2906,17 @@ GenerateCallSetter(JSContext* cx, IonScr } static bool IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject holder, MutableHandleShape shape) { MOZ_ASSERT(IsCacheableDOMProxy(obj)); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); if (!checkObj) return false; if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address())) return false; if (!holder) return false; @@ -3024,30 +3029,30 @@ GenerateAddSlot(JSContext* cx, MacroAsse newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty(); // Guard that the incoming value is in the type set for the property // if a type barrier is required. if (checkTypeset) CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures); // Guard shapes along prototype chain. - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); Register protoReg = tempReg; bool first = true; while (proto) { Shape* protoShape = proto->as<NativeObject>().lastProperty(); // Load next prototype. masm.loadObjProto(first ? object : protoReg, protoReg); first = false; // Ensure that its shape matches. masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, failures); - proto = proto->getProto(); + proto = proto->staticPrototype(); } // Call a stub to (re)allocate dynamic slots, if necessary. uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>() ? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots() : obj->as<NativeObject>().numDynamicSlots(); if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) { AllocatableRegisterSet regs(RegisterSet::Volatile()); @@ -3243,17 +3248,17 @@ IsPropertySetInlineable(NativeObject* ob } static bool PrototypeChainShadowsPropertyAdd(JSContext* cx, JSObject* obj, jsid id) { // Walk up the object prototype chain and ensure that all prototypes // are native, and that all prototypes have no getter or setter // defined on the property - for (JSObject* proto = obj->getProto(); proto; proto = proto->getProto()) { + for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) { // If prototype is non-native, don't optimize if (!proto->isNative()) return true; // If prototype defines this property in a non-plain way, don't optimize Shape* protoShape = proto->as<NativeObject>().lookupPure(id); if (protoShape && !protoShape->hasDefaultSetter()) return true; @@ -3844,17 +3849,17 @@ GetPropertyIC::canAttachDenseElementHole do { if (obj->isIndexed()) return false; if (ClassCanHaveExtraProperties(obj->getClass())) return false; - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto) break; if (!proto->isNative()) return false; // Make sure objects on the prototype don't have dense elements. if (proto->as<NativeObject>().getDenseInitializedLength() != 0) @@ -3880,20 +3885,20 @@ GenerateDenseElementHole(JSContext* cx, attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, Address(object, JSObject::offsetOfShape()), 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, ImmGCPtr(obj->getProto()), &failures); + masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), &failures); } - JSObject* pobj = obj->getProto(); + JSObject* pobj = obj->staticPrototype(); while (pobj) { MOZ_ASSERT(pobj->as<NativeObject>().lastProperty()); masm.movePtr(ImmGCPtr(pobj), scratchReg); // Non-singletons with uncacheable protos can change their proto // without a shape change, so also guard on the group (which determines // the proto) in this case. @@ -3909,17 +3914,17 @@ GenerateDenseElementHole(JSContext* cx, // 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); - pobj = pobj->getProto(); + pobj = pobj->staticPrototype(); } // Ensure the index is an int32 value. Register indexReg; if (index.hasValue()) { // Unbox the index. ValueOperand val = index.valueReg(); masm.branchTestInt32(Assembler::NotEqual, val, &failures); @@ -4313,25 +4318,25 @@ IsDenseElementSetInlineable(JSObject* ob return false; // The object may have a setter definition, // either directly, or via a prototype, or via the target object for a prototype // which is a proxy, that handles a particular integer write. // Scan the prototype and shape chain to make sure that this is not the case. JSObject* curObj = obj; while (curObj) { - // Ensure object is native. + // Ensure object is native. (This guarantees static prototype below.) if (!curObj->isNative()) return false; // Ensure all indexed properties are stored in dense elements. if (curObj->isIndexed()) return false; - curObj = curObj->getProto(); + curObj = curObj->staticPrototype(); } *checkTypeset = false; if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, JSID_VOID, val, checkTypeset)) return false; return true; }
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -2059,17 +2059,17 @@ IonBuilder::inlineObjectCreate(CallInfo& if (!templateObject) return InliningStatus_NotInlined; MOZ_ASSERT(templateObject->is<PlainObject>()); MOZ_ASSERT(!templateObject->isSingleton()); // Ensure the argument matches the template object's prototype. MDefinition* arg = callInfo.getArg(0); - if (JSObject* proto = templateObject->getProto()) { + if (JSObject* proto = templateObject->staticPrototype()) { if (IsInsideNursery(proto)) return InliningStatus_NotInlined; TemporaryTypeSet* types = arg->resultTypeSet(); if (!types || types->maybeSingleton() != proto) return InliningStatus_NotInlined; MOZ_ASSERT(types->getKnownMIRType() == MIRType::Object);
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5559,17 +5559,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont // If this access has never executed, try to add types to the observed set // according to any property which exists on the object or its prototype. if (observed->empty() && name) { JSObject* obj; if (key->isSingleton()) obj = key->singleton(); else - obj = key->proto().isLazy() ? nullptr : key->proto().toObjectOrNull(); + obj = key->proto().isDynamic() ? nullptr : key->proto().toObjectOrNull(); while (obj) { if (!obj->getClass()->isNative()) break; TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj); if (propertycx) key->ensureTrackedProperty(propertycx, NameToId(name)); @@ -5583,17 +5583,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont if (types.length() == 1) { // Note: the return value here is ignored. observed->addType(types[0], GetJitContext()->temp->lifoAlloc()); break; } } } - obj = obj->getProto(); + obj = obj->staticPrototype(); } } // If any objects which could be observed are similar to ones that have // already been observed, add them to the observed type set. if (!key->unknownProperties()) { HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID); @@ -5771,17 +5771,17 @@ PrototypeHasIndexedProperty(IonBuilder* TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(builder->checkNurseryObject(obj)); if (ClassCanHaveExtraProperties(key->clasp())) return true; if (key->unknownProperties()) return true; HeapTypeSetKey index = key->property(JSID_VOID); if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints())) return true; - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); return false; } // Whether Array.prototype, or an object on its proto chain, has an indexed property. bool jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -2177,17 +2177,17 @@ StripPreliminaryObjectStubs(JSContext* c iter.unlink(cx); } } JSObject* GetDOMProxyProto(JSObject* obj) { MOZ_ASSERT(IsCacheableDOMProxy(obj)); - return obj->getTaggedProto().toObjectOrNull(); + return obj->staticPrototype(); } // Look up a property's shape on an object, being careful never to do any effectful // operations. This procedure not yielding a shape should not be taken as a lack of // existence of the property on the object. bool EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject holder, MutableHandleShape shape, @@ -2248,40 +2248,35 @@ IsCacheableProtoChain(JSObject* obj, JSO if (!obj->is<UnboxedPlainObject>() && !obj->is<UnboxedArrayObject>() && !obj->is<TypedObject>()) { return false; } } - // Don't handle objects which require a prototype guard. This should - // be uncommon so handling it is likely not worth the complexity. - if (obj->hasUncacheableProto()) - return false; - JSObject* cur = obj; while (cur != holder) { // We cannot assume that we find the holder object on the prototype // chain and must check for null proto. The prototype chain can be // altered during the lookupProperty call. - JSObject* proto; - if (isDOMProxy && cur == obj) - proto = cur->getTaggedProto().toObjectOrNull(); - else - proto = cur->getProto(); - + MOZ_ASSERT(!cur->hasDynamicPrototype()); + + // Don't handle objects which require a prototype guard. This should + // be uncommon so handling it is likely not worth the complexity. + if (cur->hasUncacheableProto()) + return false; + + JSObject* proto = cur->staticPrototype(); if (!proto || !proto->isNative()) return false; - if (proto->hasUncacheableProto()) - return false; - cur = proto; } + return true; } bool IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, Shape* shape, bool isDOMProxy) { if (!shape || !IsCacheableProtoChain(obj, holder, isDOMProxy)) return false; @@ -2709,17 +2704,17 @@ CheckHasNoSuchProperty(JSContext* cx, JS return false; } else if (curObj->is<TypedObject>()) { if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name))) return false; } else { return false; } - JSObject* proto = curObj->getTaggedProto().toObjectOrNull(); + JSObject* proto = curObj->staticPrototype(); if (!proto) break; curObj = proto; depth++; } if (lastProto) @@ -3138,23 +3133,25 @@ ICGetPropNativeCompiler::generateStubCod masm.bind(&failure); EmitStubGuardFailure(masm); return true; } bool GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle<ShapeVector> shapes) { - JSObject* curProto = obj->getProto(); + JSObject* curProto = obj->staticPrototype(); for (size_t i = 0; i < protoChainDepth; i++) { if (!shapes.append(curProto->as<NativeObject>().lastProperty())) return false; - curProto = curProto->getProto(); + curProto = curProto->staticPrototype(); } - MOZ_ASSERT(!curProto); + + MOZ_ASSERT(!curProto, + "longer prototype chain encountered than this stub permits!"); return true; } bool ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm) { MOZ_ASSERT(engine_ == Engine::Baseline);
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1128,17 +1128,17 @@ JS_ResolveStandardClass(JSContext* cx, H JS_PUBLIC_API(bool) JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj) { MOZ_ASSERT_IF(maybeObj, maybeObj->is<GlobalObject>()); // The global object's resolve hook is special: JS_ResolveStandardClass // initializes the prototype chain lazily. Only attempt to optimize here // if we know the prototype chain has been initialized. - if (!maybeObj || !maybeObj->getProto()) + if (!maybeObj || !maybeObj->staticPrototype()) return true; if (!JSID_IS_ATOM(id)) return false; JSAtom* atom = JSID_TO_ATOM(id); // This will return true even for deselected constructors. (To do
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -823,36 +823,43 @@ ObjectMayHaveExtraIndexedOwnProperties(J { return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) || obj->isIndexed() || obj->is<TypedArrayObject>() || ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames, obj->getClass(), INT_TO_JSID(0), obj); } +/* + * Whether obj may have indexed properties anywhere besides its dense + * elements. This includes other indexed properties in its shape hierarchy, and + * indexed properties or elements along its prototype chain. + */ bool js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) { - /* - * Whether obj may have indexed properties anywhere besides its dense - * elements. This includes other indexed properties in its shape hierarchy, - * and indexed properties or elements along its prototype chain. - */ + MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative()); if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; - while ((obj = obj->getProto()) != nullptr) { + do { + MOZ_ASSERT(obj->hasStaticPrototype(), + "dynamic-prototype objects must be non-native, ergo must " + "have failed ObjectMayHaveExtraIndexedOwnProperties"); + + obj = obj->staticPrototype(); + if (!obj) + return false; // no extra indexed properties found + if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0) return true; - } - - return false; + } while (true); } static bool AddLengthProperty(ExclusiveContext* cx, HandleArrayObject obj) { /* * Add the 'length' property for a newly created array, * and update the elements to be an empty array owned by the object. @@ -2687,17 +2694,17 @@ GetIndexedPropertiesInRange(JSContext* c *success = false; // First, look for proxies or class hooks that can introduce extra // properties. JSObject* pobj = obj; do { if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty()) return true; - } while ((pobj = pobj->getProto())); + } while ((pobj = pobj->staticPrototype())); // Collect indexed property names. pobj = obj; do { // Append dense elements. NativeObject* nativeObj = &pobj->as<NativeObject>(); uint32_t initLen = nativeObj->getDenseInitializedLength(); for (uint32_t i = begin; i < initLen && i < end; i++) { @@ -2732,17 +2739,17 @@ GetIndexedPropertiesInRange(JSContext* c // Watch out for getters, they can add new properties. if (!shape.hasDefaultGetter()) return true; if (!indexes.append(i)) return false; } } - } while ((pobj = pobj->getProto())); + } while ((pobj = pobj->staticPrototype())); // Sort the indexes. Vector<uint32_t> tmp(cx); size_t n = indexes.length(); if (!tmp.resize(n)) return false; if (!MergeSort(indexes.begin(), n, tmp.begin(), SortComparatorIndexes())) return false; @@ -3596,17 +3603,17 @@ js::NewPartlyAllocatedArrayTryUseGroup(E template <uint32_t maxLength> static inline JSObject* NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, NewObjectKind newKind = GenericObject, bool forceAnalyze = false) { if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>()) return NewArray<maxLength>(cx, length, nullptr, newKind); - if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) + if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype()) return NewArray<maxLength>(cx, length, nullptr, newKind); RootedObjectGroup group(cx, obj->getGroup(cx)); if (!group) return nullptr; return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind, forceAnalyze); }
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -451,17 +451,17 @@ JSCompartment::wrap(JSContext* cx, Mutab obj.set(&p->value().get().toObject()); MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>()); return true; } RootedObject existing(cx, existingArg); if (existing) { // Is it possible to reuse |existing|? - if (!existing->getTaggedProto().isLazy() || + if (existing->hasStaticPrototype() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || obj->isCallable()) { existing = nullptr; } }
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -342,18 +342,17 @@ js::GetGlobalForObjectCrossCompartment(J { return &obj->global(); } JS_FRIEND_API(JSObject*) js::GetPrototypeNoProxy(JSObject* obj) { MOZ_ASSERT(!obj->is<js::ProxyObject>()); - MOZ_ASSERT(!obj->getTaggedProto().isLazy()); - return obj->getTaggedProto().toObjectOrNull(); + return obj->staticPrototype(); } JS_FRIEND_API(void) js::AssertSameCompartment(JSContext* cx, JSObject* obj) { assertSameCompartment(cx, obj); }
--- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2002,17 +2002,17 @@ js::CloneFunctionReuseScript(JSContext* } else { clone->initNative(fun->native(), fun->jitInfo()); } /* * Clone the function, reusing its script. We can use the same group as * the original function provided that its prototype is correct. */ - if (fun->getProto() == clone->getProto()) + if (fun->staticPrototype() == clone->staticPrototype()) clone->setGroup(fun->group()); return clone; } JSFunction* js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent, HandleObject newStaticScope, gc::AllocKind allocKind /* = FUNCTION */, @@ -2155,18 +2155,18 @@ void js::ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clasp) { RootedValue thisv(cx, call.thisv()); #ifdef DEBUG if (thisv.isObject()) { MOZ_ASSERT(thisv.toObject().getClass() != clasp || !thisv.toObject().isNative() || - !thisv.toObject().getProto() || - thisv.toObject().getProto()->getClass() != clasp); + !thisv.toObject().staticPrototype() || + thisv.toObject().staticPrototype()->getClass() != clasp); } else if (thisv.isString()) { MOZ_ASSERT(clasp != &StringObject::class_); } else if (thisv.isNumber()) { MOZ_ASSERT(clasp != &NumberObject::class_); } else if (thisv.isBoolean()) { MOZ_ASSERT(clasp != &BooleanObject::class_); } else if (thisv.isSymbol()) { MOZ_ASSERT(clasp != &SymbolObject::class_);
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -106,18 +106,20 @@ Enumerate(JSContext* cx, HandleObject po // If we've already seen this, we definitely won't add it. IdSet::AddPtr p = ht->lookupForAdd(id); if (MOZ_UNLIKELY(!!p)) return true; // It's not necessary to add properties to the hash table at the end of // the prototype chain, but custom enumeration behaviors might return // duplicated properties, so always add in such cases. - if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOpsEnumerate()) && !ht->add(p, id)) - return false; + if (pobj->is<ProxyObject>() || pobj->staticPrototype() || pobj->getOpsEnumerate()) { + if (!ht->add(p, id)) + return false; + } } // Symbol-keyed properties and nonenumerable properties are skipped unless // the caller specifically asks for them. A caller can also filter out // non-symbols by asking for JSITER_SYMBOLSONLY. if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY)) return true; if (!enumerable && !(flags & JSITER_HIDDEN)) @@ -696,17 +698,21 @@ VectorToKeyIterator(JSContext* cx, Handl return false; if (numGuards) { // Fill in the guard array from scratch. JSObject* pobj = obj; size_t ind = 0; do { ni->guard_array[ind++].init(ReceiverGuard(pobj)); - pobj = pobj->getProto(); + + // The one caller of this method that passes |numGuards > 0|, does + // so only if the entire chain consists of cacheable objects (that + // necessarily have static prototypes). + pobj = pobj->staticPrototype(); } while (pobj); MOZ_ASSERT(ind == numGuards); } objp.set(iterobj); RegisterEnumerator(cx, iterobj, ni); return true; @@ -838,47 +844,46 @@ js::GetIterator(JSContext* cx, HandleObj // iterated over. PropertyIteratorObject* last = cx->runtime()->nativeIterCache.last; if (last) { NativeIterator* lastni = last->getNativeIterator(); if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && CanCompareIterableObjectToCache(obj) && ReceiverGuard(obj) == lastni->guard_array[0]) { - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (CanCompareIterableObjectToCache(proto) && ReceiverGuard(proto) == lastni->guard_array[1] && - !proto->getProto()) + !proto->staticPrototype()) { objp.set(last); UpdateNativeIterator(lastni, obj); RegisterEnumerator(cx, last, lastni); return true; } } } - /* - * The iterator object for JSITER_ENUMERATE never escapes, so we - * don't care for the proper parent/proto to be set. This also - * allows us to re-use a previous iterator object that is not - * currently active. - */ + // The iterator object for JSITER_ENUMERATE never escapes, so we don't + // care that the "proper" prototype is set. This also lets us reuse an + // old, inactive iterator object. { JSObject* pobj = obj; do { if (!CanCacheIterableObject(cx, pobj)) { guards.clear(); goto miss; } + ReceiverGuard guard(pobj); key = (key + (key << 16)) ^ guard.hash(); if (!guards.append(guard)) return false; - pobj = pobj->getProto(); + + pobj = pobj->staticPrototype(); } while (pobj); } PropertyIteratorObject* iterobj = cx->runtime()->nativeIterCache.get(key); if (iterobj) { NativeIterator* ni = iterobj->getNativeIterator(); if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && ni->guard_key == key &&
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -496,17 +496,17 @@ js::SetIntegrityLevel(JSContext* cx, Han HandleNativeObject nobj = obj.as<NativeObject>(); // Seal/freeze non-dictionary objects by constructing a new shape // hierarchy mirroring the original one, which can be shared if many // objects with the same structure are sealed/frozen. If we use the // generic path below then any non-empty object will be converted to // dictionary mode. RootedShape last(cx, EmptyShape::getInitialShape(cx, nobj->getClass(), - nobj->getTaggedProto(), + nobj->taggedProto(), nobj->numFixedSlots(), nobj->lastProperty()->getObjectFlags())); if (!last) return false; // Get an in-order list of the shapes in this object. Rooted<ShapeVector> shapes(cx, ShapeVector(cx)); for (Shape::Range<NoGC> r(nobj->lastProperty()); !r.empty(); r.popFront()) { @@ -684,17 +684,17 @@ NewObject(ExclusiveContext* cx, HandleOb return obj; } void NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto, gc::AllocKind kind, NativeObject* obj) { MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>()); - MOZ_ASSERT(obj->getTaggedProto() == proto); + MOZ_ASSERT(obj->taggedProto() == proto); return fill(entry, clasp, proto.raw(), kind, obj); } bool js::NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto, NewObjectKind newKind, const Class* clasp) { return cxArg->isJSContext() && @@ -909,17 +909,17 @@ CreateThisForFunctionWithGroup(JSContext RootedPlainObject templateObject(cx, newScript->templateObject()); MOZ_ASSERT(templateObject->group() == group); RootedPlainObject res(cx, CopyInitializerObject(cx, templateObject, newKind)); if (!res) return nullptr; if (newKind == SingletonObject) { - Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->getProto())); + Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->staticPrototype())); if (!res->splicePrototype(cx, &PlainObject::class_, proto)) return nullptr; } else { res->setGroup(group); } return res; } @@ -1336,23 +1336,23 @@ InitializePropertiesFromCompatibleNative uint32_t initialized = src->getDenseInitializedLength(); for (uint32_t i = 0; i < initialized; ++i) { dst->setDenseInitializedLength(i + 1); dst->initDenseElement(i, src->getDenseElement(i)); } MOZ_ASSERT(!src->hasPrivate()); RootedShape shape(cx); - if (src->getProto() == dst->getProto()) { + if (src->staticPrototype() == dst->staticPrototype()) { shape = src->lastProperty(); } else { // We need to generate a new shape for dst that has dst's proto but all // the property information from src. Note that we asserted above that // dst's object flags are 0. - shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->getTaggedProto(), + shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(), dst->numFixedSlots(), 0); if (!shape) return false; // Get an in-order list of the shapes in the src object. Rooted<ShapeVector> shapes(cx, ShapeVector(cx)); for (Shape::Range<NoGC> r(src->lastProperty()); !r.empty(); r.popFront()) { if (!shapes.append(&r.front())) @@ -1938,17 +1938,17 @@ js::SetClassAndProto(JSContext* cx, Hand return false; } if (!obj->isDelegate()) { // If |obj| is not a proto of another object, we don't need to // reshape the whole proto chain. MOZ_ASSERT(obj == oldproto); break; } - oldproto = oldproto->getProto(); + oldproto = oldproto->staticPrototype(); } if (proto.isObject() && !proto.toObject()->setDelegate(cx)) return false; if (obj->isSingleton()) { /* * Just splice the prototype, but mark the properties as unknown for @@ -1988,17 +1988,17 @@ js::SetClassAndProto(JSContext* cx, Hand /* static */ bool JSObject::changeToSingleton(JSContext* cx, HandleObject obj) { MOZ_ASSERT(!obj->isSingleton()); MarkObjectGroupUnknownProperties(cx, obj->group()); ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(), - obj->getTaggedProto()); + obj->taggedProto()); if (!group) return false; obj->group_ = group; return true; } static bool @@ -2290,17 +2290,17 @@ js::LookupPropertyPure(ExclusiveContext* return true; } if (isTypedArrayOutOfRange) { *objp = nullptr; return true; } - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); *objp = nullptr; *propp = nullptr; return true; } bool @@ -2493,23 +2493,23 @@ JSObject::reportNotExtensible(JSContext* JSDVG_IGNORE_STACK, val, nullptr, nullptr, nullptr); } bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary, MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is<js::ProxyObject>()); return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop); } *isOrdinary = true; - protop.set(obj->getTaggedProto().toObjectOrNull()); + protop.set(obj->staticPrototype()); return true; } // Our immutable-prototype behavior is non-standard, and it's unclear whether // it's shippable. (Or at least it's unclear whether it's shippable with any // provided-by-default uses exposed to script.) If this bool is true, // immutable-prototype behavior is enforced; if it's false, behavior is not // enforced, and immutable-prototype bits stored on objects are completely @@ -2522,29 +2522,25 @@ JS_ImmutablePrototypesEnabled() return ImmutablePrototypesEnabled; } /*** ES6 standard internal methods ***************************************************************/ bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::ObjectOpResult& result) { - /* - * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's - * {get,set}Prototype and setImmutablePrototype methods mediate access to - * |obj.[[Prototype]]|. The Proxy subsystem is responsible for responding - * to such attempts. - */ - if (obj->hasLazyPrototype()) { + // The proxy trap subsystem fully handles prototype-setting for proxies + // with dynamic [[Prototype]]s. + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is<ProxyObject>()); return Proxy::setPrototype(cx, obj, proto, result); } /* Disallow mutation of immutable [[Prototype]]s. */ - if (obj->nonLazyPrototypeIsImmutable() && ImmutablePrototypesEnabled) + if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled) return result.fail(JSMSG_CANT_SET_PROTO); /* * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which * due to their complicated delegate-object shenanigans can't easily * have a mutable [[Prototype]]. */ if (obj->is<ArrayBufferObject>()) { @@ -2571,17 +2567,17 @@ js::SetPrototype(JSContext* cx, HandleOb "incompatible Location object"); return false; } /* * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return true. * Since the values in question are objects, we can just compare pointers. */ - if (proto == obj->getProto()) + if (proto == obj->staticPrototype()) return result.succeed(); /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ bool extensible; if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return result.fail(JSMSG_CANT_SET_PROTO); @@ -2781,17 +2777,17 @@ js::DefineElement(ExclusiveContext* cx, } /*** SpiderMonkey nonstandard internal methods ***************************************************/ bool js::SetImmutablePrototype(ExclusiveContext* cx, HandleObject obj, bool* succeeded) { - if (obj->hasLazyPrototype()) { + if (obj->hasDynamicPrototype()) { if (!cx->shouldBeJSContext()) return false; return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded); } if (!obj->setFlags(cx, BaseShape::IMMUTABLE_PROTOTYPE)) return false; *succeeded = true; @@ -3466,17 +3462,18 @@ JSObject::dump() if (obj->isQualifiedVarObj()) fprintf(stderr, " varobj"); if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj"); if (obj->watched()) fprintf(stderr, " watched"); if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton"); if (obj->isNewGroupUnknown()) fprintf(stderr, " new_type_unknown"); if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto"); if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access"); if (obj->wasNewScriptCleared()) fprintf(stderr, " new_script_cleared"); - if (!obj->hasLazyPrototype() && obj->nonLazyPrototypeIsImmutable()) fprintf(stderr, " immutable_prototype"); + if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) + fprintf(stderr, " immutable_prototype"); if (obj->isNative()) { NativeObject* nobj = &obj->as<NativeObject>(); if (nobj->inDictionaryMode()) fprintf(stderr, " inDictionaryMode"); if (nobj->hasShapeTable()) fprintf(stderr, " hasShapeTable"); } @@ -3492,19 +3489,19 @@ JSObject::dump() dumpValue(nobj->getDenseElement(i)); fprintf(stderr, "\n"); fflush(stderr); } } } fprintf(stderr, "proto "); - TaggedProto proto = obj->getTaggedProto(); - if (proto.isLazy()) - fprintf(stderr, "<lazy>"); + TaggedProto proto = obj->taggedProto(); + if (proto.isDynamic()) + fprintf(stderr, "<dynamic>"); else dumpValue(ObjectOrNullValue(proto.toObjectOrNull())); fputc('\n', stderr); if (clasp->flags & JSCLASS_HAS_PRIVATE) fprintf(stderr, "private %p\n", obj->as<NativeObject>().getPrivate()); if (!obj->isNative())
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -260,16 +260,19 @@ class JSObject : public js::gc::Cell // Objects with an uncacheable proto can have their prototype mutated // without inducing a shape change on the object. JIT inline caches should // do an explicit group guard to guard against this. Singletons always // generate a new shape when their prototype changes, regardless of this // hasUncacheableProto flag. inline bool hasUncacheableProto() const; bool setUncacheableProto(js::ExclusiveContext* cx) { + MOZ_ASSERT(hasStaticPrototype(), + "uncacheability as a concept is only applicable to static " + "(not dynamically-computed) prototypes"); 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. */ inline bool hadElementsAccess() const; @@ -350,68 +353,71 @@ class JSObject : public js::gc::Cell inline js::ObjectGroup* getGroup(JSContext* cx); const js::HeapPtrObjectGroup& groupFromGC() const { /* Direct field access for use by GC. */ return group_; } /* - * We allow the prototype of an object to be lazily computed if the object - * is a proxy. In the lazy case, we store (JSObject*)0x1 in the proto field - * of the object's group. We offer three ways of getting the prototype: + * We permit proxies to dynamically compute their prototype if desired. + * (Not all proxies will so desire: in particular, most DOM proxies can + * track their prototype with a single, nullable JSObject*.) If a proxy + * so desires, we store (JSObject*)0x1 in the proto field of the object's + * group. * - * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy - * with a relevant getPrototype() handler. - * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to + * We offer three ways to get an object's prototype: + * + * 1. obj->staticPrototype() returns the prototype, but it asserts if obj + * is a proxy, and the proxy has opted to dynamically compute its + * prototype using a getPrototype() handler. + * 2. obj->taggedProto() returns a TaggedProto, which can be tested to * check if the proto is an object, nullptr, or lazily computed. * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object. - * If obj is a proxy and the proto is lazy, this code may allocate or - * GC in order to compute the proto. Currently, it will not run JS code. + * If obj is a proxy with dynamically-computed prototype, this code may + * perform arbitrary behavior (allocation, GC, run JS) while computing + * the proto. */ - js::TaggedProto getTaggedProto() const { + js::TaggedProto taggedProto() const { return group_->proto(); } bool hasTenuredProto() const; bool uninlinedIsProxy() const; - JSObject* getProto() const { - MOZ_ASSERT(!hasLazyPrototype()); - return getTaggedProto().toObjectOrNull(); + JSObject* staticPrototype() const { + MOZ_ASSERT(hasStaticPrototype()); + return taggedProto().toObjectOrNull(); + } + + // Normal objects and a subset of proxies have an uninteresting, static + // (albeit perhaps mutable) [[Prototype]]. For such objects the + // [[Prototype]] is just a value returned when needed for accesses, or + // modified in response to requests. These objects store the + // [[Prototype]] directly within |obj->group_|. + bool hasStaticPrototype() const { + return !hasDynamicPrototype(); } - // Normal objects and a subset of proxies have uninteresting [[Prototype]]. - // For such objects the [[Prototype]] is just a value returned when needed - // for accesses, or modified in response to requests. These objects store - // the [[Prototype]] directly within |obj->type_|. - // - // Proxies that don't have such a simple [[Prototype]] instead have a - // "lazy" [[Prototype]]. Accessing the [[Prototype]] of such an object - // requires going through the proxy handler {get,set}Prototype and - // setImmutablePrototype methods. This is most commonly useful for proxies - // that are wrappers around other objects. If the [[Prototype]] of the - // underlying object changes, the [[Prototype]] of the wrapper must also - // simultaneously change. We implement this by having the handler methods - // simply delegate to the wrapped object, forwarding its response to the - // caller. - // - // This method returns true if this object has a non-simple [[Prototype]] - // as described above, or false otherwise. - bool hasLazyPrototype() const { - bool lazy = getTaggedProto().isLazy(); - MOZ_ASSERT_IF(lazy, uninlinedIsProxy()); - return lazy; + // The remaining proxies have a [[Prototype]] requiring dynamic computation + // for every access, going through the proxy handler {get,set}Prototype and + // setImmutablePrototype methods. (Wrappers particularly use this to keep + // the wrapper/wrappee [[Prototype]]s consistent.) + bool hasDynamicPrototype() const { + bool dynamic = taggedProto().isDynamic(); + MOZ_ASSERT_IF(dynamic, uninlinedIsProxy()); + MOZ_ASSERT_IF(dynamic, !isNative()); + return dynamic; } - // True iff this object's [[Prototype]] is immutable. Must not be called - // on proxies with lazy [[Prototype]]! - inline bool nonLazyPrototypeIsImmutable() const; + // True iff this object's [[Prototype]] is immutable. Must be called only + // on objects with a static [[Prototype]]! + inline bool staticPrototypeIsImmutable() 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. */
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -112,17 +112,17 @@ JSObject::finalize(js::FreeOp* fop) } /* 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(), - obj->getTaggedProto()); + obj->taggedProto()); if (!group) return false; obj->group_ = group; return true; } inline js::ObjectGroup* @@ -147,23 +147,23 @@ JSObject::setGroup(js::ObjectGroup* grou } /*** Standard internal methods *******************************************************************/ inline bool js::GetPrototype(JSContext* cx, js::HandleObject obj, js::MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is<js::ProxyObject>()); return js::Proxy::getPrototype(cx, obj, protop); - } else { - protop.set(obj->getTaggedProto().toObjectOrNull()); - return true; } + + protop.set(obj->taggedProto().toObjectOrNull()); + return true; } inline bool js::IsExtensible(ExclusiveContext* cx, HandleObject obj, bool* extensible) { if (obj->is<ProxyObject>()) { if (!cx->shouldBeJSContext()) return false; @@ -482,19 +482,19 @@ JSObject::hadElementsAccess() const inline bool JSObject::isIndexed() const { return hasAllFlags(js::BaseShape::INDEXED); } inline bool -JSObject::nonLazyPrototypeIsImmutable() const +JSObject::staticPrototypeIsImmutable() const { - MOZ_ASSERT(!hasLazyPrototype()); + MOZ_ASSERT(hasStaticPrototype()); return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE); } inline bool JSObject::isIteratedSingleton() const { return hasAllFlags(js::BaseShape::ITERATED_SINGLETON); } @@ -559,17 +559,17 @@ IsNativeFunction(const js::Value& v, JSN */ static MOZ_ALWAYS_INLINE bool ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid methodid, JSNative native) { MOZ_ASSERT(obj->getClass() == clasp); Value v; if (!HasDataProperty(cx, obj, methodid, &v)) { - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as<NativeObject>(), methodid, &v)) return false; } return IsNativeFunction(v, native); } // Return whether looking up 'valueOf' on 'obj' definitely resolves to the @@ -580,17 +580,17 @@ HasObjectValueOf(JSObject* obj, JSContex { if (obj->is<ProxyObject>() || !obj->isNative()) return false; jsid valueOf = NameToId(cx->names().valueOf); Value v; while (!HasDataProperty(cx, &obj->as<NativeObject>(), valueOf, &v)) { - obj = obj->getProto(); + obj = obj->staticPrototype(); if (!obj || obj->is<ProxyObject>() || !obj->isNative()) return false; } return IsNativeFunction(v, obj_valueOf); } /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
--- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -391,36 +391,36 @@ JSObject* BaseProxyHandler::weakmapKeyDelegate(JSObject* proxy) const { return nullptr; } bool BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const { - MOZ_CRASH("must override getPrototype with lazy prototype"); + MOZ_CRASH("must override getPrototype with dynamic prototype"); } bool BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) const { - // Disallow sets of protos on proxies with lazy protos, but no hook. + // Disallow sets of protos on proxies with dynamic prototypes but no hook. // This keeps us away from the footgun of having the first proto set opt // you out of having dynamic protos altogether. JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF, "incompatible Proxy"); return false; } bool BaseProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject protop) const { - MOZ_CRASH("must override getPrototypeIfOrdinary with lazy prototype"); + MOZ_CRASH("must override getPrototypeIfOrdinary with dynamic prototype"); } bool BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const { *succeeded = false; return true; }
--- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -178,34 +178,34 @@ js::AppendUnique(JSContext* cx, AutoIdVe uniqueOthers.append(others[i]); } return base.appendAll(uniqueOthers); } /* static */ bool Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto) { - MOZ_ASSERT(proxy->hasLazyPrototype()); + MOZ_ASSERT(proxy->hasDynamicPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto); } /* static */ bool Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result) { - MOZ_ASSERT(proxy->hasLazyPrototype()); + MOZ_ASSERT(proxy->hasDynamicPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result); } /* static */ bool Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, MutableHandleObject proto) { - MOZ_ASSERT(proxy->hasLazyPrototype()); + MOZ_ASSERT(proxy->hasDynamicPrototype()); JS_CHECK_RECURSION(cx, return false); return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary, proto); } /* static */ bool Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) { @@ -777,17 +777,17 @@ js::NewProxyObject(JSContext* cx, const } void ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv) { MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this)); MOZ_ASSERT(getClass() == &ProxyObject::proxyClass); MOZ_ASSERT(!IsWindowProxy(this)); - MOZ_ASSERT(hasLazyPrototype()); + MOZ_ASSERT(hasDynamicPrototype()); setHandler(handler); setCrossCompartmentPrivate(priv); setExtra(0, UndefinedValue()); setExtra(1, UndefinedValue()); } JS_FRIEND_API(JSObject*)
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3358,30 +3358,32 @@ END_CASE(JSOP_DEFFUN) CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); JSObject* obj = Lambda(cx, fun, REGS.fp()->scopeChain()); if (!obj) goto error; - MOZ_ASSERT(obj->getProto()); + + MOZ_ASSERT(obj->staticPrototype()); PUSH_OBJECT(*obj); } END_CASE(JSOP_LAMBDA) CASE(JSOP_LAMBDA_ARROW) { /* Load the specified function object literal. */ ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc))); ReservedRooted<Value> newTarget(&rootValue1, REGS.sp[-1]); JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), newTarget); if (!obj) goto error; - MOZ_ASSERT(obj->getProto()); + + MOZ_ASSERT(obj->staticPrototype()); REGS.sp[-1].setObject(*obj); } END_CASE(JSOP_LAMBDA_ARROW) CASE(JSOP_CALLEE) MOZ_ASSERT(REGS.fp()->isFunctionFrame()); PUSH_COPY(REGS.fp()->calleev()); END_CASE(JSOP_CALLEE)
--- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -561,17 +561,17 @@ LookupPropertyInline(ExclusiveContext* c if (done) { if (propp) objp.set(current); else objp.set(nullptr); return true; } - typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto()); + typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->staticPrototype()); if (!proto) break; if (!proto->isNative()) { if (!cx->shouldBeJSContext() || !allowGC) return false; return LookupProperty(cx->asJSContext(), MaybeRooted<JSObject*, allowGC>::toHandle(proto),
--- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1075,17 +1075,17 @@ PurgeProtoChain(ExclusiveContext* cx, JS /* Lookups will not be cached through non-native protos. */ if (!obj->isNative()) break; shape = obj->as<NativeObject>().lookup(cx, id); if (shape) return obj->as<NativeObject>().shadowingShapeChange(cx, *shape); - obj = obj->getProto(); + obj = obj->staticPrototype(); } return true; } static bool PurgeScopeChainHelper(ExclusiveContext* cx, HandleObject objArg, HandleId id) { @@ -1094,17 +1094,17 @@ PurgeScopeChainHelper(ExclusiveContext* MOZ_ASSERT(obj->isNative()); MOZ_ASSERT(obj->isDelegate()); /* Lookups on integer ids cannot be cached through prototypes. */ if (JSID_IS_INT(id)) return true; - if (!PurgeProtoChain(cx, obj->getProto(), id)) + if (!PurgeProtoChain(cx, obj->staticPrototype(), id)) return false; /* * We must purge the scope chain only for Call objects as they are the only * kind of cacheable non-global object that can gain properties after outer * properties with the same names have been cached or traced. Call objects * may gain such properties via eval introducing new vars; see bug 490364. */ @@ -1635,17 +1635,17 @@ js::NativeHasProperty(JSContext* cx, Han // done can be true in exactly these unlikely-sounding cases: // - We're looking up an element, and pobj is a TypedArray that // doesn't have that many elements. // - We're being called from a resolve hook to assign to the property // being resolved. // What they all have in common is we do not want to keep walking // the prototype chain, and always claim that the property // doesn't exist. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); // Step 8. if (!proto) { *foundp = false; return true; } // Step 7.a. If the prototype is also native, this step is a @@ -2007,17 +2007,17 @@ NativeGetPropertyInline(JSContext* cx, // Steps 4.a-b. The check for 'done' on this next line is tricky. // done can be true in exactly these unlikely-sounding cases: // - We're looking up an element, and pobj is a TypedArray that // doesn't have that many elements. // - We're being called from a resolve hook to assign to the property // being resolved. // What they all have in common is we do not want to keep walking // the prototype chain. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); // Step 4.c. The spec algorithm simply returns undefined if proto is // null, but see the comment on GetNonexistentProperty. if (!proto) return GetNonexistentProperty(cx, obj, id, receiver, nameLookup, vp); // Step 4.d. If the prototype is also native, this step is a // recursive tail call, and we don't need to go through all the @@ -2217,19 +2217,20 @@ js::SetPropertyByDefining(JSContext* cx, // When setting |id| for |receiver| and |obj| has no property for id, continue // the search up the prototype chain. bool js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result) { MOZ_ASSERT(!obj->is<ProxyObject>()); - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (proto) return SetProperty(cx, proto, id, v, receiver, result); + return SetPropertyByDefining(cx, id, v, receiver, result); } /* * Implement "the rest of" assignment to a property when no property receiver[id] * was found anywhere on the prototype chain. * * FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9, @@ -2393,17 +2394,17 @@ js::NativeSetProperty(JSContext* cx, Han // Steps 4.a-b. The check for 'done' on this next line is tricky. // done can be true in exactly these unlikely-sounding cases: // - We're looking up an element, and pobj is a TypedArray that // doesn't have that many elements. // - We're being called from a resolve hook to assign to the property // being resolved. // What they all have in common is we do not want to keep walking // the prototype chain. - RootedObject proto(cx, done ? nullptr : pobj->getProto()); + RootedObject proto(cx, done ? nullptr : pobj->staticPrototype()); if (!proto) { // Step 4.d.i (and step 5). return SetNonexistentProperty(cx, id, v, receiver, qualified, result); } // Step 4.c.i. If the prototype is also native, this step is a // recursive tail call, and we don't need to go through all the // plumbing of SetProperty; the top of the loop is where we're going to
--- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -255,17 +255,17 @@ bool JSObject::shouldSplicePrototype(JSContext* cx) { /* * During bootstrapping, if inference is enabled we need to make sure not * to splice a new prototype in for Function.prototype or the global * object if their __proto__ had previously been set to null, as this * will change the prototype for all other objects with the same type. */ - if (getProto() != nullptr) + if (staticPrototype() != nullptr) return false; return isSingleton(); } bool JSObject::splicePrototype(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto) { MOZ_ASSERT(cx->compartment() == compartment()); @@ -322,17 +322,17 @@ JSObject::makeLazyGroup(JSContext* cx, H initialFlags |= OBJECT_FLAG_ITERATED; if (obj->isIndexed()) initialFlags |= OBJECT_FLAG_SPARSE_INDEXES; if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX) initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; - Rooted<TaggedProto> proto(cx, obj->getTaggedProto()); + Rooted<TaggedProto> proto(cx, obj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto, initialFlags); if (!group) return nullptr; AutoEnterAnalysis enter(cx); /* Fill in the type according to the state of this object. */ @@ -496,17 +496,17 @@ ObjectGroup::defaultNewGroup(ExclusiveCo MOZ_ASSERT_IF(clasp, group->clasp() == clasp); MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || group->clasp() == &UnboxedPlainObject::class_); MOZ_ASSERT(group->proto() == proto); return group; } ObjectGroupFlags initialFlags = 0; - if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown())) + if (proto.isDynamic() || (proto.isObject() && proto.toObject()->isNewGroupUnknown())) initialFlags = OBJECT_FLAG_DYNAMIC_MASK; Rooted<TaggedProto> protoRoot(cx, proto); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_, protoRoot, initialFlags); if (!group) return nullptr; @@ -1215,17 +1215,17 @@ ObjectGroup::newPlainObject(ExclusiveCon return nullptr; // Don't make entries with duplicate property names, which will show up // here as objects with fewer properties than we thought we were // adding to the object. In this case, reset the object's group to the // default (which will have unknown properties) so that the group we // just created will be collected by the GC. if (obj->slotSpan() != nproperties) { - ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->getTaggedProto()); + ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->taggedProto()); if (!group) return nullptr; obj->setGroup(group); return obj; } // Keep track of the initial objects we create with this type. // If the initial ones have a consistent shape and property types, we
--- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -94,16 +94,20 @@ class ObjectGroup : public gc::TenuredCe const Class* clasp() const { return clasp_; } void setClasp(const Class* clasp) { clasp_ = clasp; } + bool hasDynamicPrototype() const { + return proto_.isDynamic(); + } + const HeapPtr<TaggedProto>& proto() const { return proto_; } HeapPtr<TaggedProto>& proto() { return proto_; }
--- a/js/src/vm/PIC.cpp +++ b/js/src/vm/PIC.cpp @@ -176,24 +176,17 @@ js::ForOfPIC::Chain::getMatchingStub(JSO return nullptr; } bool js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj) { MOZ_ASSERT(obj->is<ArrayObject>()); - - // Ensure object's prototype is the actual Array.prototype - if (!obj->getTaggedProto().isObject()) - return false; - if (obj->getTaggedProto().toObject() != arrayProto_) - return false; - - return true; + return obj->staticPrototype() == arrayProto_; } bool js::ForOfPIC::Chain::isArrayStateStillSane() { // Ensure that canonical Array.prototype has matching shape. if (arrayProto_->lastProperty() != arrayProtoShape_) return false;
--- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -844,17 +844,17 @@ RegExpCompartment::createMatchResultTemp /* Create template array object */ RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount, nullptr, TenuredObject)); if (!templateObject) return matchResultTemplateObject_; // = nullptr // Create a new group for the template. - Rooted<TaggedProto> proto(cx, templateObject->getTaggedProto()); + Rooted<TaggedProto> proto(cx, templateObject->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto); if (!group) return matchResultTemplateObject_; // = nullptr templateObject->setGroup(group); /* Set dummy index property */ RootedValue index(cx, Int32Value(0)); if (!NativeDefineProperty(cx, templateObject, cx->names().index, index, nullptr, nullptr,
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -892,17 +892,17 @@ class NonSyntacticVariablesObject : publ Handle<ClonedBlockObject*> globalLexical); }; class NestedScopeObject : public ScopeObject { public: // Return the static scope corresponding to this scope chain object. inline NestedStaticScope* staticScope() { - return &getProto()->as<NestedStaticScope>(); + return &staticPrototype()->as<NestedStaticScope>(); } void initEnclosingScope(JSObject* obj) { MOZ_ASSERT(getReservedSlot(SCOPE_CHAIN_SLOT).isUndefined()); setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(obj)); } }; @@ -922,17 +922,17 @@ class DynamicWithObject : public NestedS NonSyntacticWith }; static DynamicWithObject* create(JSContext* cx, HandleObject object, HandleObject enclosing, HandleObject staticWith, WithKind kind = SyntacticWith); StaticWithScope& staticWith() const { - return getProto()->as<StaticWithScope>(); + return staticPrototype()->as<StaticWithScope>(); } /* Return the 'o' in 'with (o)'. */ JSObject& object() const { return getReservedSlot(OBJECT_SLOT).toObject(); } /* Return object for GetThisValue. */ @@ -1002,17 +1002,17 @@ class ClonedBlockObject : public NestedS void setSlotValue(unsigned i, const Value& v) { setSlot(RESERVED_SLOTS + i, v); } public: /* The static block from which this block was cloned. */ StaticBlockScope& staticBlock() const { - return getProto()->as<StaticBlockScope>(); + return staticPrototype()->as<StaticBlockScope>(); } /* Assuming 'put' has been called, return the value of the ith let var. */ const Value& var(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) { MOZ_ASSERT_IF(checkAliasing, staticBlock().isAliased(i)); return slotValue(i); } @@ -1382,17 +1382,17 @@ class DebugScopes }; } /* namespace js */ template<> inline bool JSObject::is<js::StaticBlockScope>() const { - return hasClass(&js::ClonedBlockObject::class_) && !getProto(); + return hasClass(&js::ClonedBlockObject::class_) && !staticPrototype(); } template<> inline bool JSObject::is<js::NestedStaticScope>() const { return is<js::StaticBlockScope>() || is<js::StaticWithScope>(); @@ -1406,17 +1406,17 @@ JSObject::is<js::StaticScope>() const is<js::StaticEvalScope>() || is<js::StaticNonSyntacticScope>(); } template<> inline bool JSObject::is<js::ClonedBlockObject>() const { - return hasClass(&js::ClonedBlockObject::class_) && !!getProto(); + return hasClass(&js::ClonedBlockObject::class_) && staticPrototype(); } template<> inline bool JSObject::is<js::NestedScopeObject>() const { return is<js::ClonedBlockObject>() || is<js::DynamicWithObject>();
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -474,17 +474,17 @@ intrinsic_FinishBoundFunctionInit(JSCont if (targetObj->isConstructor()) bound->setIsConstructor(); // 9.4.1.3 BoundFunctionCreate, Steps 2-3., 8. RootedObject proto(cx); if (!GetPrototype(cx, targetObj, &proto)) return false; - if (bound->getProto() != proto) { + if (bound->staticPrototype() != proto) { if (!SetPrototype(cx, bound, proto)) return false; } bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]); MOZ_ASSERT(!bound->hasGuessedAtom()); // 9.2.11 SetFunctionName, Step 6.
--- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -121,17 +121,17 @@ EmptyShape::ensureInitialCustomShape(Exc // |CreateBlankProto| marked it as a delegate. These are the only objects // of this class that won't use the standard prototype, and there's no // reason to pollute the initial shape cache with entries for them. if (obj->isDelegate()) return true; // Cache the initial shape for non-prototype objects, however, so that // future instances will begin life with that shape. - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); EmptyShape::insertInitialShape(cx, shape, proto); return true; } inline AutoRooterGetterSetter::Inner::Inner(ExclusiveContext* cx, uint8_t attrs, GetterOp* pgetter_, SetterOp* psetter_) : CustomAutoRooter(cx), attrs(attrs),
--- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1189,17 +1189,17 @@ JSObject::setFlags(ExclusiveContext* cx, self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase); return true; } Shape* existingShape = self->ensureShape(cx); if (!existingShape) return false; - Shape* newShape = Shape::setObjectFlags(cx, flags, self->getTaggedProto(), existingShape); + Shape* newShape = Shape::setObjectFlags(cx, flags, self->taggedProto(), existingShape); if (!newShape) return false; self->setShapeMaybeNonNative(newShape); return true; } bool
--- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -44,15 +44,15 @@ js::HashNumber js::TaggedProto::hashCode() const { return Zone::UniqueIdToHash(uniqueId()); } uint64_t js::TaggedProto::uniqueId() const { - if (isLazy()) + if (isDynamic()) return uint64_t(1); JSObject* obj = toObjectOrNull(); if (!obj) return uint64_t(0); return obj->zone()->getUniqueIdInfallible(obj); }
--- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -19,17 +19,17 @@ class TaggedProto public: static JSObject * const LazyProto; TaggedProto() : proto(nullptr) {} explicit TaggedProto(JSObject* proto) : proto(proto) {} uintptr_t toWord() const { return uintptr_t(proto); } - bool isLazy() const { + bool isDynamic() const { return proto == LazyProto; } bool isObject() const { /* Skip nullptr and LazyProto. */ return uintptr_t(proto) > uintptr_t(TaggedProto::LazyProto); } JSObject* toObject() const { MOZ_ASSERT(isObject()); @@ -78,17 +78,17 @@ template<class Outer> class TaggedProtoOperations { const TaggedProto& value() const { return static_cast<const Outer*>(this)->get(); } public: uintptr_t toWord() const { return value().toWord(); } - inline bool isLazy() const { return value().isLazy(); } + inline bool isDynamic() const { return value().isDynamic(); } inline bool isObject() const { return value().isObject(); } inline JSObject* toObject() const { return value().toObject(); } inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); } JSObject* raw() const { return value().raw(); } HashNumber hashCode() const { return value().hashCode(); } uint64_t uniqueId() const { return value().uniqueId(); } };
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1210,17 +1210,17 @@ const Class* TypeSet::ObjectKey::clasp() { return isGroup() ? group()->clasp() : singleton()->getClass(); } TaggedProto TypeSet::ObjectKey::proto() { - return isGroup() ? group()->proto() : singleton()->getTaggedProto(); + return isGroup() ? group()->proto() : singleton()->taggedProto(); } TypeNewScript* TypeSet::ObjectKey::newScript() { if (isGroup() && group()->newScript()) return group()->newScript(); return nullptr; @@ -2415,17 +2415,17 @@ TemporaryTypeSet::getCommonPrototype(Com if (!key) continue; if (key->unknownProperties()) return false; TaggedProto nproto = key->proto(); if (isFirst) { - if (nproto.isLazy()) + if (nproto.isDynamic()) return false; *proto = nproto.toObjectOrNull(); isFirst = false; } else { if (nproto != TaggedProto(*proto)) return false; } } @@ -3002,18 +3002,21 @@ ObjectGroup::clearNewScript(ExclusiveCon } void ObjectGroup::print() { TaggedProto tagged(proto()); fprintf(stderr, "%s : %s", TypeSet::ObjectGroupString(this), - tagged.isObject() ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject())) - : (tagged.isLazy() ? "(lazy)" : "(null)")); + tagged.isObject() + ? TypeSet::TypeString(TypeSet::ObjectType(tagged.toObject())) + : tagged.isDynamic() + ? "(dynamic)" + : "(null)"); if (unknownProperties()) { fprintf(stderr, " unknown"); } else { if (!hasAnyFlags(OBJECT_FLAG_SPARSE_INDEXES)) fprintf(stderr, " dense"); if (!hasAnyFlags(OBJECT_FLAG_NON_PACKED)) fprintf(stderr, " packed"); @@ -3114,17 +3117,17 @@ js::AddClearDefiniteGetterSetterForProto } if (protoGroup->unknownProperties()) return false; HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id); if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) return false; if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(group))) return false; - proto = proto->getProto(); + proto = proto->staticPrototype(); } return true; } /* * Constraint which clears definite properties on a group should a type set * contain any types other than a single object. */ @@ -3314,17 +3317,17 @@ JSScript::makeTypes(JSContext* cx) /* static */ bool JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, bool singleton /* = false */) { if (singleton) { if (!setSingleton(cx, fun)) return false; } else { - RootedObject funProto(cx, fun->getProto()); + RootedObject funProto(cx, fun->staticPrototype()); Rooted<TaggedProto> taggedProto(cx, TaggedProto(funProto)); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, taggedProto); if (!group) return false; fun->setGroup(group); group->setInterpretedFunction(fun); @@ -3607,19 +3610,17 @@ TypeNewScript::registerNewObject(PlainOb preliminaryObjects->registerNewObject(res); } static bool ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocKind) { MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty())); - Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), - obj->getTaggedProto(), - allocKind); + Shape* newShape = ReshapeForAllocKind(cx, obj->lastProperty(), obj->taggedProto(), allocKind); if (!newShape) return false; obj->setLastPropertyShrinkFixedSlots(newShape); return true; } namespace {
--- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -716,17 +716,17 @@ UnboxedPlainObject::obj_lookupProperty(J MutableHandleShape propp) { if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) { MarkNonNativePropertyFound<CanGC>(propp); objp.set(obj); return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); return true; } return LookupProperty(cx, proto, id, objp, propp); } @@ -767,17 +767,17 @@ UnboxedPlainObject::obj_defineProperty(J /* static */ bool UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) { if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) { *foundp = true; return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; } return HasProperty(cx, proto, id, foundp); } @@ -794,17 +794,17 @@ UnboxedPlainObject::obj_getProperty(JSCo if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) { if (expando->containsShapeOrElement(cx, id)) { RootedObject nexpando(cx, expando); return GetProperty(cx, nexpando, receiver, id, vp); } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; } return GetProperty(cx, proto, receiver, id, vp); } @@ -1416,17 +1416,17 @@ UnboxedArrayObject::obj_lookupProperty(J MutableHandleShape propp) { if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { MarkNonNativePropertyFound<CanGC>(propp); objp.set(obj); return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); return true; } return LookupProperty(cx, proto, id, objp, propp); } @@ -1467,17 +1467,17 @@ UnboxedArrayObject::obj_defineProperty(J /* static */ bool UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) { if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { *foundp = true; return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; } return HasProperty(cx, proto, id, foundp); } @@ -1488,17 +1488,17 @@ UnboxedArrayObject::obj_getProperty(JSCo if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) { if (JSID_IS_INT(id)) vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id))); else vp.set(Int32Value(obj->as<UnboxedArrayObject>().length())); return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; } return GetProperty(cx, proto, receiver, id, vp); }