diff --git a/dom/base/WindowNamedPropertiesHandler.h b/dom/base/WindowNamedPropertiesHandler.h index 628a27b608ad..32b926f97cb1 100644 --- a/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -37,9 +37,8 @@ public: delete_(JSContext* aCx, JS::Handle aProxy, JS::Handle 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 aProxy, diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5a765df143c1..1d8db144b822 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -925,13 +925,14 @@ nsOuterWindowProxy::getPrototypeIfOrdinary(JSContext* cx, // // 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; } diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 522340f3235d..71aa3ea86da4 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -232,7 +232,7 @@ MapIteratorObject::createResultPair(JSContext* cx) if (!resultPairObj) return nullptr; - Rooted proto(cx, resultPairObj->getTaggedProto()); + Rooted proto(cx, resultPairObj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, resultPairObj->getClass(), proto); if (!group) return nullptr; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 101eeb8b1450..587285d18110 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -611,7 +611,7 @@ js::ObjectCreateImpl(JSContext* cx, HandleObject proto, NewObjectKind 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); } diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 5ec36bed248f..effda51c0dd8 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -1609,12 +1609,12 @@ js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, u 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; } diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 95539441de42..a5db65d5213a 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1679,7 +1679,7 @@ TypedObject::obj_lookupProperty(JSContext* cx, HandleObject obj, HandleId id, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -1751,7 +1751,7 @@ TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -1808,7 +1808,7 @@ TypedObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiv } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -1836,7 +1836,7 @@ TypedObject::obj_getElement(JSContext* cx, HandleObject obj, HandleValue receive return obj_getArrayElement(cx, typedObj, descr, index, vp); } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -2019,7 +2019,7 @@ TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, Ob 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(); diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 1e9dbee7b84b..49ef86436cdc 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -530,7 +530,8 @@ class TypedObject : public JSObject public: TypedProto& typedProto() const { - return getProto()->as(); + // Typed objects' prototypes can't be modified. + return staticPrototype()->as(); } TypeDescr& typeDescr() const { diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index d10fe1cdf49e..4d8e26f21d4a 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -785,12 +785,14 @@ IsCacheableSetPropAddSlot(JSContext* cx, JSObject* obj, Shape* oldShape, 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().lookup(cx, id); if (protoShape && !protoShape->hasDefaultSetter()) @@ -2352,13 +2354,13 @@ SetElemAddHasSameShapes(ICSetElem_DenseOrUnboxedArrayAdd* stub, JSObject* obj) 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().lastProperty() != nstub->shape(i + 1)) return false; - proto = obj->getProto(); + proto = obj->staticPrototype(); if (!proto) { if (i != stub->protoChainDepth() - 1) return false; @@ -2479,12 +2481,12 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, // 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) @@ -3698,7 +3700,7 @@ TryAttachGlobalNameValueStub(JSContext* cx, HandleScript script, jsbytecode* pc, if (current == globalLexical) { current = &globalLexical->global(); } else { - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is()) return true; current = &proto->as(); @@ -3773,7 +3775,7 @@ TryAttachGlobalNameAccessorStub(JSContext* cx, HandleScript script, jsbytecode* shape = current->lookup(cx, id); if (shape) break; - JSObject* proto = current->getProto(); + JSObject* proto = current->staticPrototype(); if (!proto || !proto->is()) return true; current = &proto->as(); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index c376dd5456ad..075f4b0eaefc 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -137,12 +137,10 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* 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(); + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; @@ -151,12 +149,12 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, 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(); } } @@ -199,12 +197,12 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, // 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().lastProperty()); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastObjId = protoId; } } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 0ab8a3032e85..563a6ab61ac3 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -6271,7 +6271,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee) return nullptr; if (!templateObject->is() && !templateObject->is()) return nullptr; - if (templateObject->getProto() != proto) + if (templateObject->staticPrototype() != proto) return nullptr; TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group()); @@ -6318,7 +6318,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee) 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()); @@ -8106,7 +8106,7 @@ IonBuilder::testSingletonProperty(JSObject* obj, jsid id) if (ObjectHasExtraOwnProperty(compartment, objKey, id)) return nullptr; - obj = checkNurseryObject(obj->getProto()); + obj = checkNurseryObject(obj->staticPrototype()); } return nullptr; diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index a4e8d4b1cd90..b2c3b0072b45 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -422,12 +422,10 @@ GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSO // 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(); + JSObject* pobj = obj->staticPrototype(); if (!pobj) return; while (pobj != holder) { @@ -438,12 +436,14 @@ GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSO // 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(); } } @@ -459,7 +459,7 @@ jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder) * 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; @@ -500,7 +500,7 @@ IsCacheableNoProperty(JSObject* obj, JSObject* holder, Shape* shape, jsbytecode* 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 @@ -819,19 +819,20 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, } 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().lastProperty()), &prototypeFailures); - proto = proto->getProto(); + proto = proto->staticPrototype(); lastReg = scratchReg; } @@ -1803,7 +1804,7 @@ GetPropertyIC::tryAttachDOMProxyUnshadowed(JSContext* cx, HandleScript outerScri MOZ_ASSERT(monitoredResult()); MOZ_ASSERT(output().hasValue()); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); RootedNativeObject holder(cx); RootedShape shape(cx); @@ -2906,7 +2907,7 @@ IsCacheableDOMProxyUnshadowedSetterCall(JSContext* cx, HandleObject obj, HandleI { MOZ_ASSERT(IsCacheableDOMProxy(obj)); - RootedObject checkObj(cx, obj->getTaggedProto().toObjectOrNull()); + RootedObject checkObj(cx, obj->staticPrototype()); if (!checkObj) return false; @@ -3029,7 +3030,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att 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) { @@ -3042,7 +3043,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att // 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. @@ -3248,7 +3249,7 @@ 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; @@ -3849,7 +3850,7 @@ GetPropertyIC::canAttachDenseElementHole(JSObject* obj, HandleValue idval, Typed if (ClassCanHaveExtraProperties(obj->getClass())) return false; - JSObject* proto = obj->getProto(); + JSObject* proto = obj->staticPrototype(); if (!proto) break; @@ -3885,10 +3886,10 @@ GenerateDenseElementHole(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta 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().lastProperty()); @@ -3914,7 +3915,7 @@ GenerateDenseElementHole(JSContext* cx, MacroAssembler& masm, IonCache::StubAtta 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. @@ -4318,7 +4319,7 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegiste // 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; @@ -4326,7 +4327,7 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegiste if (curObj->isIndexed()) return false; - curObj = curObj->getProto(); + curObj = curObj->staticPrototype(); } *checkTypeset = false; diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 99bcd4519e34..76b726d808de 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -2064,7 +2064,7 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo) // 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; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 3225cc1e0fe8..5db14df89177 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -5623,7 +5623,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, 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()) @@ -5647,7 +5647,7 @@ jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx, } } - obj = obj->getProto(); + obj = obj->staticPrototype(); } } @@ -5835,7 +5835,7 @@ PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj) 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; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 7bddeb5b7fe8..231bc69a0c7f 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -2172,7 +2172,7 @@ 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 @@ -2243,30 +2243,25 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy) } } - // 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()); - if (!proto || !proto->isNative()) + // 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; - if (proto->hasUncacheableProto()) + JSObject* proto = cur->staticPrototype(); + if (!proto || !proto->isNative()) return false; cur = proto; } + return true; } @@ -2630,7 +2625,7 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name, return false; } - JSObject* proto = curObj->getTaggedProto().toObjectOrNull(); + JSObject* proto = curObj->staticPrototype(); if (!proto) break; @@ -3049,13 +3044,15 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler& masm) bool GetProtoShapes(JSObject* obj, size_t protoChainDepth, MutableHandle shapes) { - JSObject* curProto = obj->getProto(); + JSObject* curProto = obj->staticPrototype(); for (size_t i = 0; i < protoChainDepth; i++) { if (!shapes.append(curProto->as().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; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ae23a970355b..7ca3d2724924 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1133,7 +1133,7 @@ JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj // 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)) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 9bde6ce8291d..6fba17fca9a2 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -828,26 +828,33 @@ ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) 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 @@ -2696,7 +2703,7 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint32_t begin, uin 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; @@ -2741,7 +2748,7 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint32_t begin, uin return false; } } - } while ((pobj = pobj->getProto())); + } while ((pobj = pobj->staticPrototype())); // Sort the indexes. Vector tmp(cx); @@ -3605,7 +3612,7 @@ NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, if (!obj->is() && !obj->is()) return NewArray(cx, length, nullptr, newKind); - if (obj->getProto() != cx->global()->maybeGetArrayPrototype()) + if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype()) return NewArray(cx, length, nullptr, newKind); RootedObjectGroup group(cx, obj->getGroup(cx)); diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index c24d1cde021b..5fa60f15ad14 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -456,7 +456,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existin 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()) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 8e6fc32cf369..e0aca12ced78 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -353,8 +353,7 @@ JS_FRIEND_API(JSObject*) js::GetPrototypeNoProxy(JSObject* obj) { MOZ_ASSERT(!obj->is()); - MOZ_ASSERT(!obj->getTaggedProto().isLazy()); - return obj->getTaggedProto().toObjectOrNull(); + return obj->staticPrototype(); } JS_FRIEND_API(void) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a8475f79be28..3161b3575cdc 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2007,7 +2007,7 @@ js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject par * 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; } @@ -2160,8 +2160,8 @@ js::ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clas 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()) { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 3bb8aada2fb5..962b06b74b93 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -111,8 +111,10 @@ Enumerate(JSContext* cx, HandleObject pobj, jsid id, // 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() || pobj->getProto() || pobj->getOpsEnumerate()) && !ht->add(p, id)) - return false; + if (pobj->is() || pobj->staticPrototype() || pobj->getOpsEnumerate()) { + if (!ht->add(p, id)) + return false; + } } // Symbol-keyed properties and nonenumerable properties are skipped unless @@ -701,7 +703,11 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto 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); } @@ -843,10 +849,10 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb 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); @@ -856,12 +862,9 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb } } - /* - * 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 { @@ -869,11 +872,13 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleOb 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); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 0c2d7940ec69..334bf001b060 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -501,7 +501,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) // 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) @@ -689,7 +689,7 @@ NewObjectCache::fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto gc::AllocKind kind, NativeObject* obj) { MOZ_ASSERT_IF(proto.isObject(), !proto.toObject()->is()); - MOZ_ASSERT(obj->getTaggedProto() == proto); + MOZ_ASSERT(obj->taggedProto() == proto); return fill(entry, clasp, proto.raw(), kind, obj); } @@ -914,7 +914,7 @@ CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, return nullptr; if (newKind == SingletonObject) { - Rooted proto(cx, TaggedProto(templateObject->getProto())); + Rooted proto(cx, TaggedProto(templateObject->staticPrototype())); if (!res->splicePrototype(cx, &PlainObject::class_, proto)) return nullptr; } else { @@ -1341,13 +1341,13 @@ InitializePropertiesFromCompatibleNativeObject(JSContext* cx, 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; @@ -1943,7 +1943,7 @@ js::SetClassAndProto(JSContext* cx, HandleObject obj, MOZ_ASSERT(obj == oldproto); break; } - oldproto = oldproto->getProto(); + oldproto = oldproto->staticPrototype(); } if (proto.isObject() && !proto.toObject()->setDelegate(cx)) @@ -1993,7 +1993,7 @@ JSObject::changeToSingleton(JSContext* cx, HandleObject obj) MarkObjectGroupUnknownProperties(cx, obj->group()); ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(), - obj->getTaggedProto()); + obj->taggedProto()); if (!group) return false; @@ -2295,7 +2295,7 @@ js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** return true; } - obj = obj->getProto(); + obj = obj->staticPrototype(); } while (obj); *objp = nullptr; @@ -2498,13 +2498,13 @@ bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary, MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is()); return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop); } *isOrdinary = true; - protop.set(obj->getTaggedProto().toObjectOrNull()); + protop.set(obj->staticPrototype()); return true; } @@ -2527,19 +2527,15 @@ JS_ImmutablePrototypesEnabled() 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()); 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); /* @@ -2576,7 +2572,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object * 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]]. */ @@ -2786,7 +2782,7 @@ js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, Handle 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); @@ -3471,7 +3467,8 @@ JSObject::dump() 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(); @@ -3497,9 +3494,9 @@ JSObject::dump() } fprintf(stderr, "proto "); - TaggedProto proto = obj->getTaggedProto(); - if (proto.isLazy()) - fprintf(stderr, ""); + TaggedProto proto = obj->taggedProto(); + if (proto.isDynamic()) + fprintf(stderr, ""); else dumpValue(ObjectOrNullValue(proto.toObjectOrNull())); fputc('\n', stderr); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 845681ad9db6..bbaf785b7e03 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -265,6 +265,9 @@ class JSObject : public js::gc::Cell // 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); } @@ -355,20 +358,26 @@ class JSObject : public js::gc::Cell } /* - * 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(); } @@ -376,37 +385,34 @@ class JSObject : public js::gc::Cell 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 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; + // 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(); } - // True iff this object's [[Prototype]] is immutable. Must not be called - // on proxies with lazy [[Prototype]]! - inline bool nonLazyPrototypeIsImmutable() const; + // 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 be called only + // on objects with a static [[Prototype]]! + inline bool staticPrototypeIsImmutable() const; inline void setGroup(js::ObjectGroup* group); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index c607349ee74b..a38bdc937552 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -117,7 +117,7 @@ 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; @@ -152,13 +152,13 @@ JSObject::setGroup(js::ObjectGroup* group) inline bool js::GetPrototype(JSContext* cx, js::HandleObject obj, js::MutableHandleObject protop) { - if (obj->getTaggedProto().isLazy()) { + if (obj->hasDynamicPrototype()) { MOZ_ASSERT(obj->is()); return js::Proxy::getPrototype(cx, obj, protop); - } else { - protop.set(obj->getTaggedProto().toObjectOrNull()); - return true; } + + protop.set(obj->taggedProto().toObjectOrNull()); + return true; } inline bool @@ -487,9 +487,9 @@ JSObject::isIndexed() const } inline bool -JSObject::nonLazyPrototypeIsImmutable() const +JSObject::staticPrototypeIsImmutable() const { - MOZ_ASSERT(!hasLazyPrototype()); + MOZ_ASSERT(hasStaticPrototype()); return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE); } @@ -564,7 +564,7 @@ ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid m 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(), methodid, &v)) return false; } @@ -585,7 +585,7 @@ HasObjectValueOf(JSObject* obj, JSContext* cx) Value v; while (!HasDataProperty(cx, &obj->as(), valueOf, &v)) { - obj = obj->getProto(); + obj = obj->staticPrototype(); if (!obj || obj->is() || !obj->isNative()) return false; } diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index ec0d392fd664..e19fa6c2137f 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -396,14 +396,14 @@ BaseProxyHandler::weakmapKeyDelegate(JSObject* proxy) const 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, @@ -415,7 +415,7 @@ 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 diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index ba5f52bdf94e..baf93dddb8be 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -183,7 +183,7 @@ js::AppendUnique(JSContext* cx, AutoIdVector& base, AutoIdVector& others) /* 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().handler()->getPrototype(cx, proxy, proto); } @@ -191,7 +191,7 @@ Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject 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().handler()->setPrototype(cx, proxy, proto, result); } @@ -200,7 +200,7 @@ Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, Objec 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().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary, proto); @@ -782,7 +782,7 @@ 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); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index fa2314e04aa9..ef9935398634 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3363,7 +3363,8 @@ CASE(JSOP_LAMBDA) 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) @@ -3376,7 +3377,8 @@ CASE(JSOP_LAMBDA_ARROW) 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) diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 177256834f6b..05d82b8b92e9 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -566,7 +566,7 @@ LookupPropertyInline(ExclusiveContext* cx, return true; } - typename MaybeRooted::RootType proto(cx, current->getProto()); + typename MaybeRooted::RootType proto(cx, current->staticPrototype()); if (!proto) break; diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 84c5110b1f26..d9d7ec1becae 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1086,7 +1086,7 @@ PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id) if (shape) return obj->as().shadowingShapeChange(cx, *shape); - obj = obj->getProto(); + obj = obj->staticPrototype(); } return true; @@ -1105,7 +1105,7 @@ PurgeScopeChainHelper(ExclusiveContext* cx, HandleObject objArg, HandleId id) if (JSID_IS_INT(id)) return true; - if (!PurgeProtoChain(cx, obj->getProto(), id)) + if (!PurgeProtoChain(cx, obj->staticPrototype(), id)) return false; /* @@ -1646,7 +1646,7 @@ js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* // 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) { @@ -2018,7 +2018,7 @@ NativeGetPropertyInline(JSContext* cx, // 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. @@ -2228,9 +2228,10 @@ js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue { MOZ_ASSERT(!obj->is()); - 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); } @@ -2404,7 +2405,7 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handle // 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); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 300378c9020e..6bfee22c7253 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -260,7 +260,7 @@ JSObject::shouldSplicePrototype(JSContext* cx) * 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(); } @@ -327,7 +327,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj) if (obj->is() && obj->as().length() > INT32_MAX) initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW; - Rooted proto(cx, obj->getTaggedProto()); + Rooted proto(cx, obj->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto, initialFlags); if (!group) @@ -501,7 +501,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, } 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 protoRoot(cx, proto); @@ -1220,7 +1220,7 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ // 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); diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index 9c5dd39f3dc0..82befe49c4e7 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -99,6 +99,10 @@ class ObjectGroup : public gc::TenuredCell clasp_ = clasp; } + bool hasDynamicPrototype() const { + return proto_.isDynamic(); + } + const HeapPtr& proto() const { return proto_; } diff --git a/js/src/vm/PIC.cpp b/js/src/vm/PIC.cpp index 86da0b9e795d..587a80cd0d16 100644 --- a/js/src/vm/PIC.cpp +++ b/js/src/vm/PIC.cpp @@ -181,14 +181,7 @@ bool js::ForOfPIC::Chain::isOptimizableArray(JSObject* obj) { MOZ_ASSERT(obj->is()); - - // 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 diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 9b7f42c5e405..c15fcccbe94f 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -849,7 +849,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext* cx) return matchResultTemplateObject_; // = nullptr // Create a new group for the template. - Rooted proto(cx, templateObject->getTaggedProto()); + Rooted proto(cx, templateObject->taggedProto()); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto); if (!group) return matchResultTemplateObject_; // = nullptr diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index d41eb8f0b362..b2e1bcfab97f 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -897,7 +897,7 @@ class NestedScopeObject : public ScopeObject public: // Return the static scope corresponding to this scope chain object. inline NestedStaticScope* staticScope() { - return &getProto()->as(); + return &staticPrototype()->as(); } void initEnclosingScope(JSObject* obj) { @@ -927,7 +927,7 @@ class DynamicWithObject : public NestedScopeObject WithKind kind = SyntacticWith); StaticWithScope& staticWith() const { - return getProto()->as(); + return staticPrototype()->as(); } /* Return the 'o' in 'with (o)'. */ @@ -1007,7 +1007,7 @@ class ClonedBlockObject : public NestedScopeObject public: /* The static block from which this block was cloned. */ StaticBlockScope& staticBlock() const { - return getProto()->as(); + return staticPrototype()->as(); } /* Assuming 'put' has been called, return the value of the ith let var. */ @@ -1387,7 +1387,7 @@ template<> inline bool JSObject::is() const { - return hasClass(&js::ClonedBlockObject::class_) && !getProto(); + return hasClass(&js::ClonedBlockObject::class_) && !staticPrototype(); } template<> @@ -1411,7 +1411,7 @@ template<> inline bool JSObject::is() const { - return hasClass(&js::ClonedBlockObject::class_) && !!getProto(); + return hasClass(&js::ClonedBlockObject::class_) && staticPrototype(); } template<> diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 7a8465f89305..d3e619ccf5c6 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -479,7 +479,7 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp) if (!GetPrototype(cx, targetObj, &proto)) return false; - if (bound->getProto() != proto) { + if (bound->staticPrototype() != proto) { if (!SetPrototype(cx, bound, proto)) return false; } diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 92e0b9f54a90..e44459f110f6 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -134,7 +134,7 @@ EmptyShape::ensureInitialCustomShape(ExclusiveContext* cx, HandlegetProto()); + RootedObject proto(cx, obj->staticPrototype()); EmptyShape::insertInitialShape(cx, shape, proto); return true; } diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index be919c5c9194..50b2bbf03162 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1194,7 +1194,7 @@ JSObject::setFlags(ExclusiveContext* cx, BaseShape::Flag flags, GenerateShape ge 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; diff --git a/js/src/vm/TaggedProto.cpp b/js/src/vm/TaggedProto.cpp index a7c79aff276d..bde4a5057671 100644 --- a/js/src/vm/TaggedProto.cpp +++ b/js/src/vm/TaggedProto.cpp @@ -49,7 +49,7 @@ js::TaggedProto::hashCode() const uint64_t js::TaggedProto::uniqueId() const { - if (isLazy()) + if (isDynamic()) return uint64_t(1); JSObject* obj = toObjectOrNull(); if (!obj) diff --git a/js/src/vm/TaggedProto.h b/js/src/vm/TaggedProto.h index 675b21ddac0d..cad32482214f 100644 --- a/js/src/vm/TaggedProto.h +++ b/js/src/vm/TaggedProto.h @@ -24,7 +24,7 @@ class TaggedProto uintptr_t toWord() const { return uintptr_t(proto); } - bool isLazy() const { + bool isDynamic() const { return proto == LazyProto; } bool isObject() const { @@ -83,7 +83,7 @@ class TaggedProtoOperations 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(); } diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 7d6163e56990..fe6780b03f9f 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1215,7 +1215,7 @@ TypeSet::ObjectKey::clasp() TaggedProto TypeSet::ObjectKey::proto() { - return isGroup() ? group()->proto() : singleton()->getTaggedProto(); + return isGroup() ? group()->proto() : singleton()->taggedProto(); } TypeNewScript* @@ -2420,7 +2420,7 @@ TemporaryTypeSet::getCommonPrototype(CompilerConstraintList* constraints, JSObje TaggedProto nproto = key->proto(); if (isFirst) { - if (nproto.isLazy()) + if (nproto.isDynamic()) return false; *proto = nproto.toObjectOrNull(); isFirst = false; @@ -3007,8 +3007,11 @@ 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"); @@ -3119,7 +3122,7 @@ js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* gr return false; if (!protoTypes->addConstraint(cx, cx->typeLifoAlloc().new_(group))) return false; - proto = proto->getProto(); + proto = proto->staticPrototype(); } return true; } @@ -3319,7 +3322,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, if (!setSingleton(cx, fun)) return false; } else { - RootedObject funProto(cx, fun->getProto()); + RootedObject funProto(cx, fun->staticPrototype()); Rooted taggedProto(cx, TaggedProto(funProto)); ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, &JSFunction::class_, taggedProto); @@ -3612,9 +3615,7 @@ ChangeObjectFixedSlotCount(JSContext* cx, PlainObject* obj, gc::AllocKind allocK { 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; diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index bf6e492589dd..0fb869f58f2b 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -727,7 +727,7 @@ UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -778,7 +778,7 @@ UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -805,7 +805,7 @@ UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue } } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true; @@ -1428,7 +1428,7 @@ UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { objp.set(nullptr); propp.set(nullptr); @@ -1479,7 +1479,7 @@ UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { *foundp = false; return true; @@ -1500,7 +1500,7 @@ UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue return true; } - RootedObject proto(cx, obj->getProto()); + RootedObject proto(cx, obj->staticPrototype()); if (!proto) { vp.setUndefined(); return true;