author | Jan de Mooij <jdemooij@mozilla.com> |
Thu, 15 Apr 2021 10:11:51 +0000 | |
changeset 576199 | c50f3fb04aa3baa887716b48bafbb9e5ea58eb0e |
parent 576198 | 642e059b633f2fe06644be7f0681b229c50ecfaf |
child 576200 | 9715e3dfb584fd8507f8a3ddbda7cad2b16fe703 |
push id | 141143 |
push user | jdemooij@mozilla.com |
push date | Thu, 15 Apr 2021 10:14:43 +0000 |
treeherder | autoland@4560ff4a728b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jonco |
bugs | 1704744 |
milestone | 89.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -232,23 +232,23 @@ IRGenerator::IRGenerator(JSContext* cx, GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode, CacheKind cacheKind, HandleValue val, HandleValue idVal) : IRGenerator(cx, script, pc, cacheKind, mode), val_(val), idVal_(idVal) {} static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderId, - NativeObject* holder, Shape* shape) { - if (holder->isFixedSlot(shape->slot())) { + NativeObject* holder, ShapeProperty prop) { + if (holder->isFixedSlot(prop.slot())) { writer.loadFixedSlotResult(holderId, - NativeObject::getFixedSlotOffset(shape->slot())); + NativeObject::getFixedSlotOffset(prop.slot())); } else { size_t dynamicSlotOffset = - holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); + holder->dynamicSlotIndex(prop.slot()) * sizeof(Value); writer.loadDynamicSlotResult(holderId, dynamicSlotOffset); } } // DOM proxies // ----------- // // DOM proxies are proxies that are used to implement various DOM objects like @@ -457,38 +457,34 @@ static bool IsCacheableProtoChain(Native } obj = &proto->as<NativeObject>(); } return true; } #endif static bool IsCacheableGetPropReadSlot(NativeObject* obj, NativeObject* holder, - Shape* shape) { - MOZ_ASSERT(shape); + ShapeProperty prop) { MOZ_ASSERT(IsCacheableProtoChain(obj, holder)); - return shape->isDataProperty(); + return prop.isDataProperty(); } enum NativeGetPropCacheability { CanAttachNone, CanAttachReadSlot, CanAttachNativeGetter, CanAttachScriptedGetter, }; static NativeGetPropCacheability IsCacheableGetPropCall(NativeObject* obj, NativeObject* holder, - Shape* shape) { - MOZ_ASSERT(shape); + ShapeProperty prop) { MOZ_ASSERT(IsCacheableProtoChain(obj, holder)); - ShapeProperty prop = ShapeProperty(shape); - if (!prop.isAccessorProperty()) { return CanAttachNone; } JSObject* getterObject = holder->getGetter(prop); if (!getterObject || !getterObject->is<JSFunction>()) { return CanAttachNone; } @@ -542,32 +538,31 @@ static bool CheckHasNoSuchProperty(JSCon curObj = curObj->staticPrototype(); } while (curObj); return true; } static bool IsCacheableNoProperty(JSContext* cx, NativeObject* obj, - NativeObject* holder, Shape* shape, jsid id, + NativeObject* holder, jsid id, jsbytecode* pc) { - MOZ_ASSERT(!shape); MOZ_ASSERT(!holder); // If we're doing a name lookup, we have to throw a ReferenceError. if (JSOp(*pc) == JSOp::GetBoundName) { return false; } return CheckHasNoSuchProperty(cx, obj, id); } static NativeGetPropCacheability CanAttachNativeGetProp( JSContext* cx, JSObject* obj, JS::PropertyKey id, NativeObject** holder, - Shape** shape, jsbytecode* pc) { + Maybe<ShapeProperty>* shapeProp, jsbytecode* pc) { MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); MOZ_ASSERT(!*holder); // The lookup needs to be universally pure, otherwise we risk calling hooks // out of turn. We don't mind doing this even when purity isn't required, // because we only miss out on shape hashification, which is only a temporary // perf cost. The limits were arbitrarily set, anyways. NativeObject* baseHolder = nullptr; @@ -575,27 +570,27 @@ static NativeGetPropCacheability CanAtta if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop)) { return CanAttachNone; } auto* nobj = &obj->as<NativeObject>(); if (prop.isNativeProperty()) { MOZ_ASSERT(baseHolder); *holder = baseHolder; - *shape = prop.shapeProperty().shapeDeprecated(); - - if (IsCacheableGetPropReadSlot(nobj, *holder, *shape)) { + *shapeProp = mozilla::Some(prop.shapeProperty()); + + if (IsCacheableGetPropReadSlot(nobj, *holder, shapeProp->ref())) { return CanAttachReadSlot; } - return IsCacheableGetPropCall(nobj, *holder, *shape); + return IsCacheableGetPropCall(nobj, *holder, shapeProp->ref()); } if (!prop.isFound()) { - if (IsCacheableNoProperty(cx, nobj, *holder, *shape, id, pc)) { + if (IsCacheableNoProperty(cx, nobj, *holder, id, pc)) { return CanAttachReadSlot; } } return CanAttachNone; } static void GuardReceiverProto(CacheIRWriter& writer, NativeObject* obj, @@ -832,41 +827,41 @@ static void EmitReadSlotGuard(CacheIRWri } } else { holderId->emplace(objId); } } template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal> static void EmitReadSlotResult(CacheIRWriter& writer, NativeObject* obj, - NativeObject* holder, Shape* shape, + NativeObject* holder, Maybe<ShapeProperty> prop, ObjOperandId objId) { Maybe<ObjOperandId> holderId; EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId, &holderId); // Slot access. if (holder) { MOZ_ASSERT(holderId->valid()); - EmitLoadSlotResult(writer, *holderId, holder, shape); + EmitLoadSlotResult(writer, *holderId, holder, *prop); } else { MOZ_ASSERT(holderId.isNothing()); writer.loadUndefinedResult(); } } static void EmitCallGetterResultNoGuards(JSContext* cx, CacheIRWriter& writer, NativeObject* obj, - NativeObject* holder, Shape* shape, + NativeObject* holder, + ShapeProperty prop, ValOperandId receiverId) { - ShapeProperty prop = ShapeProperty(shape); JSFunction* target = &holder->getGetter(prop)->as<JSFunction>(); bool sameRealm = cx->realm() == target->realm(); - switch (IsCacheableGetPropCall(obj, holder, shape)) { + switch (IsCacheableGetPropCall(obj, holder, prop)) { case CanAttachNativeGetter: { writer.callNativeGetterResult(receiverId, target, sameRealm); writer.returnFromIC(); break; } case CanAttachScriptedGetter: { writer.callScriptedGetterResult(receiverId, target, sameRealm); writer.returnFromIC(); @@ -878,74 +873,73 @@ static void EmitCallGetterResultNoGuards MOZ_ASSERT_UNREACHABLE("Can't attach getter"); break; } } // See the SMDOC comment in vm/GetterSetter.h for more info on Getter/Setter // properties static void EmitGuardGetterSetterSlot(CacheIRWriter& writer, - NativeObject* holder, Shape* shape, + NativeObject* holder, ShapeProperty prop, ObjOperandId holderId, bool holderIsConstant = false) { // If the holder is guaranteed to be the same object, and it never had a // slot holding a GetterSetter mutated or deleted, its Shape will change when // that does happen so we don't need to guard on the GetterSetter. if (holderIsConstant && !holder->hadGetterSetterChange()) { return; } - size_t slot = shape->slot(); + size_t slot = prop.slot(); Value slotVal = holder->getSlot(slot); MOZ_ASSERT(slotVal.isPrivateGCThing()); if (holder->isFixedSlot(slot)) { size_t offset = NativeObject::getFixedSlotOffset(slot); writer.guardFixedSlotValue(holderId, offset, slotVal); } else { size_t offset = holder->dynamicSlotIndex(slot) * sizeof(Value); writer.guardDynamicSlotValue(holderId, offset, slotVal); } } static void EmitCallGetterResultGuards(CacheIRWriter& writer, NativeObject* obj, - NativeObject* holder, Shape* shape, + NativeObject* holder, ShapeProperty prop, ObjOperandId objId, ICState::Mode mode) { // Use the megamorphic guard if we're in megamorphic mode, except if |obj| // is a Window as GuardHasGetterSetter doesn't support this yet (Window may // require outerizing). if (mode == ICState::Mode::Specialized || IsWindow(obj)) { TestMatchingNativeReceiver(writer, obj, objId); if (obj != holder) { GeneratePrototypeGuards(writer, obj, holder, objId); // Guard on the holder's shape. ObjOperandId holderId = writer.loadObject(holder); TestMatchingHolder(writer, holder, holderId); - EmitGuardGetterSetterSlot(writer, holder, shape, holderId, + EmitGuardGetterSetterSlot(writer, holder, prop, holderId, /* holderIsConstant = */ true); } else { - EmitGuardGetterSetterSlot(writer, holder, shape, objId); - } - } else { - ShapeProperty prop = ShapeProperty(shape); + EmitGuardGetterSetterSlot(writer, holder, prop, objId); + } + } else { GetterSetter* gs = holder->getGetterSetter(prop); - writer.guardHasGetterSetter(objId, shape->propid(), gs); + writer.guardHasGetterSetter(objId, prop.shapeDeprecated()->propid(), gs); } } static void EmitCallGetterResult(JSContext* cx, CacheIRWriter& writer, NativeObject* obj, NativeObject* holder, - Shape* shape, ObjOperandId objId, + ShapeProperty prop, ObjOperandId objId, ValOperandId receiverId, ICState::Mode mode) { - EmitCallGetterResultGuards(writer, obj, holder, shape, objId, mode); - EmitCallGetterResultNoGuards(cx, writer, obj, holder, shape, receiverId); + EmitCallGetterResultGuards(writer, obj, holder, prop, objId, mode); + EmitCallGetterResultNoGuards(cx, writer, obj, holder, prop, receiverId); } static bool CanAttachDOMCall(JSContext* cx, JSJitInfo::OpType type, JSObject* obj, JSFunction* fun, ICState::Mode mode) { MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter || type == JSJitInfo::Method); @@ -981,45 +975,44 @@ static bool CanAttachDOMCall(JSContext* DOMInstanceClassHasProtoAtDepth instanceChecker = cx->runtime()->DOMcallbacks->instanceClassMatchesProto; return instanceChecker(clasp, jitInfo->protoID, jitInfo->depth); } static bool CanAttachDOMGetterSetter(JSContext* cx, JSJitInfo::OpType type, NativeObject* obj, NativeObject* holder, - Shape* shape, ICState::Mode mode) { + ShapeProperty prop, ICState::Mode mode) { MOZ_ASSERT(type == JSJitInfo::Getter || type == JSJitInfo::Setter); - ShapeProperty prop = ShapeProperty(shape); JSObject* accessor = type == JSJitInfo::Getter ? holder->getGetter(prop) : holder->getSetter(prop); JSFunction* fun = &accessor->as<JSFunction>(); return CanAttachDOMCall(cx, type, obj, fun, mode); } static void EmitCallDOMGetterResultNoGuards(CacheIRWriter& writer, - NativeObject* holder, Shape* shape, + NativeObject* holder, + ShapeProperty prop, ObjOperandId objId) { - ShapeProperty prop = ShapeProperty(shape); JSFunction* getter = &holder->getGetter(prop)->as<JSFunction>(); writer.callDOMGetterResult(objId, getter->jitInfo()); writer.returnFromIC(); } static void EmitCallDOMGetterResult(JSContext* cx, CacheIRWriter& writer, NativeObject* obj, NativeObject* holder, - Shape* shape, ObjOperandId objId) { + ShapeProperty prop, ObjOperandId objId) { // Note: this relies on EmitCallGetterResultGuards emitting a shape guard // for specialized stubs. // The shape guard ensures the receiver's Class is valid for this DOM getter. - EmitCallGetterResultGuards(writer, obj, holder, shape, objId, + EmitCallGetterResultGuards(writer, obj, holder, prop, objId, ICState::Mode::Specialized); - EmitCallDOMGetterResultNoGuards(writer, holder, shape, objId); + EmitCallDOMGetterResultNoGuards(writer, holder, prop, objId); } void GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing) { MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic); if (cacheKind_ == CacheKind::GetProp || @@ -1034,54 +1027,54 @@ void GetPropIRGenerator::attachMegamorph trackAttached("MegamorphicNativeSlot"); } AttachDecision GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId receiverId) { - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeObject* holder = nullptr; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); switch (type) { case CanAttachNone: return AttachDecision::NoAction; case CanAttachReadSlot: { auto* nobj = &obj->as<NativeObject>(); if (mode_ == ICState::Mode::Megamorphic) { attachMegamorphicNativeSlot(objId, id, holder == nullptr); return AttachDecision::Attach; } maybeEmitIdGuard(id); - EmitReadSlotResult(writer, nobj, holder, shape, objId); + EmitReadSlotResult(writer, nobj, holder, prop, objId); writer.returnFromIC(); trackAttached("NativeSlot"); return AttachDecision::Attach; } case CanAttachScriptedGetter: case CanAttachNativeGetter: { auto* nobj = &obj->as<NativeObject>(); maybeEmitIdGuard(id); if (!isSuper() && CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, nobj, - holder, shape, mode_)) { - EmitCallDOMGetterResult(cx_, writer, nobj, holder, shape, objId); + holder, *prop, mode_)) { + EmitCallDOMGetterResult(cx_, writer, nobj, holder, *prop, objId); trackAttached("DOMGetter"); return AttachDecision::Attach; } - EmitCallGetterResult(cx_, writer, nobj, holder, shape, objId, receiverId, + EmitCallGetterResult(cx_, writer, nobj, holder, *prop, objId, receiverId, mode_); trackAttached("NativeGetter"); return AttachDecision::Attach; } } MOZ_CRASH("Bad NativeGetPropCacheability"); @@ -1141,39 +1134,38 @@ AttachDecision GetPropIRGenerator::tryAt // cases. if (mode_ == ICState::Mode::Megamorphic) { return AttachDecision::NoAction; } // Now try to do the lookup on the Window (the current global). GlobalObject* windowObj = cx_->global(); NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, windowObj, id, &holder, &prop, pc_); switch (type) { case CanAttachNone: return AttachDecision::NoAction; case CanAttachReadSlot: { maybeEmitIdGuard(id); ObjOperandId windowObjId = GuardAndLoadWindowProxyWindow(writer, objId, windowObj); - EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId); + EmitReadSlotResult(writer, windowObj, holder, prop, windowObjId); writer.returnFromIC(); trackAttached("WindowProxySlot"); return AttachDecision::Attach; } case CanAttachNativeGetter: { // Make sure the native getter is okay with the IC passing the Window // instead of the WindowProxy as |this| value. - ShapeProperty prop = ShapeProperty(shape); - JSFunction* callee = &holder->getGetter(prop)->as<JSFunction>(); + JSFunction* callee = &holder->getGetter(*prop)->as<JSFunction>(); MOZ_ASSERT(callee->isNativeWithoutJitEntry()); if (!callee->hasJitInfo() || callee->jitInfo()->needsOuterizedThisObject()) { return AttachDecision::NoAction; } // If a |super| access, it is not worth the complexity to attach an IC. if (isSuper()) { @@ -1182,23 +1174,23 @@ AttachDecision GetPropIRGenerator::tryAt // Guard the incoming object is a WindowProxy and inline a getter call // based on the Window object. maybeEmitIdGuard(id); ObjOperandId windowObjId = GuardAndLoadWindowProxyWindow(writer, objId, windowObj); if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, windowObj, holder, - shape, mode_)) { - EmitCallDOMGetterResult(cx_, writer, windowObj, holder, shape, + *prop, mode_)) { + EmitCallDOMGetterResult(cx_, writer, windowObj, holder, *prop, windowObjId); trackAttached("WindowProxyDOMGetter"); } else { ValOperandId receiverId = writer.boxObject(windowObjId); - EmitCallGetterResult(cx_, writer, windowObj, holder, shape, windowObjId, + EmitCallGetterResult(cx_, writer, windowObj, holder, *prop, windowObjId, receiverId, mode_); trackAttached("WindowProxyGetter"); } return AttachDecision::Attach; } case CanAttachScriptedGetter: @@ -1238,25 +1230,25 @@ AttachDecision GetPropIRGenerator::tryAt // keep the compartment alive. RootedObject wrappedTargetGlobal(cx_, &unwrapped->nonCCWGlobal()); if (!cx_->compartment()->wrap(cx_, &wrappedTargetGlobal)) { cx_->clearPendingException(); return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; // Enter realm of target to prevent failing compartment assertions when doing // the lookup. { AutoRealm ar(cx_, unwrapped); NativeGetPropCacheability canCache = - CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &prop, pc_); if (canCache != CanAttachReadSlot) { return AttachDecision::NoAction; } } auto* unwrappedNative = &unwrapped->as<NativeObject>(); maybeEmitIdGuard(id); writer.guardIsProxy(objId); @@ -1265,18 +1257,18 @@ AttachDecision GetPropIRGenerator::tryAt // Load the object wrapped by the CCW ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId); // If the compartment of the wrapped object is different we should fail. writer.guardCompartment(wrapperTargetId, wrappedTargetGlobal, unwrappedNative->compartment()); ObjOperandId unwrappedId = wrapperTargetId; - EmitReadSlotResult<SlotReadType::CrossCompartment>( - writer, unwrappedNative, holder, shape, unwrappedId); + EmitReadSlotResult<SlotReadType::CrossCompartment>(writer, unwrappedNative, + holder, prop, unwrappedId); writer.wrapResult(); writer.returnFromIC(); trackAttached("CCWSlot"); return AttachDecision::Attach; } static bool GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray, @@ -1504,46 +1496,45 @@ AttachDecision GetPropIRGenerator::tryAt auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate()); MOZ_ASSERT(expandoAndGeneration); expandoObj = &expandoAndGeneration->expando.toObject(); } // Try to do the lookup on the expando object. NativeObject* holder = nullptr; - Shape* propShape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability canCache = - CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_); + CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &prop, pc_); if (canCache == CanAttachNone) { return AttachDecision::NoAction; } if (!holder) { return AttachDecision::NoAction; } auto* nativeExpandoObj = &expandoObj->as<NativeObject>(); MOZ_ASSERT(holder == nativeExpandoObj); maybeEmitIdGuard(id); ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape( obj, objId, expandoVal, nativeExpandoObj); if (canCache == CanAttachReadSlot) { // Load from the expando's slots. - EmitLoadSlotResult(writer, expandoObjId, nativeExpandoObj, propShape); + EmitLoadSlotResult(writer, expandoObjId, nativeExpandoObj, *prop); writer.returnFromIC(); } else { // Call the getter. Note that we pass objId, the DOM proxy, as |this| // and not the expando object. MOZ_ASSERT(canCache == CanAttachNativeGetter || canCache == CanAttachScriptedGetter); - EmitGuardGetterSetterSlot(writer, nativeExpandoObj, propShape, - expandoObjId); + EmitGuardGetterSetterSlot(writer, nativeExpandoObj, *prop, expandoObjId); EmitCallGetterResultNoGuards(cx_, writer, nativeExpandoObj, - nativeExpandoObj, propShape, receiverId); + nativeExpandoObj, *prop, receiverId); } trackAttached("DOMProxyExpando"); return AttachDecision::Attach; } AttachDecision GetPropIRGenerator::tryAttachDOMProxyShadowed( Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id) { @@ -1601,19 +1592,19 @@ AttachDecision GetPropIRGenerator::tryAt MOZ_ASSERT(IsCacheableDOMProxy(obj)); JSObject* checkObj = obj->staticPrototype(); if (!checkObj) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability canCache = - CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, checkObj, id, &holder, &prop, pc_); if (canCache == CanAttachNone) { return AttachDecision::NoAction; } auto* nativeCheckObj = &checkObj->as<NativeObject>(); maybeEmitIdGuard(id); // Guard that our expando object hasn't started shadowing this property. @@ -1625,29 +1616,29 @@ AttachDecision GetPropIRGenerator::tryAt // getprop. GeneratePrototypeGuards(writer, obj, holder, objId); // Guard on the holder of the property. ObjOperandId holderId = writer.loadObject(holder); TestMatchingHolder(writer, holder, holderId); if (canCache == CanAttachReadSlot) { - EmitLoadSlotResult(writer, holderId, holder, shape); + EmitLoadSlotResult(writer, holderId, holder, *prop); writer.returnFromIC(); } else { // EmitCallGetterResultNoGuards expects |obj| to be the object the // property is on to do some checks. Since we actually looked at // checkObj, and no extra guards will be generated, we can just // pass that instead. MOZ_ASSERT(canCache == CanAttachNativeGetter || canCache == CanAttachScriptedGetter); MOZ_ASSERT(!isSuper()); - EmitGuardGetterSetterSlot(writer, holder, shape, holderId, + EmitGuardGetterSetterSlot(writer, holder, *prop, holderId, /* holderIsConstant = */ true); - EmitCallGetterResultNoGuards(cx_, writer, nativeCheckObj, holder, shape, + EmitCallGetterResultNoGuards(cx_, writer, nativeCheckObj, holder, *prop, receiverId); } } else { // Property was not found on the prototype chain. Deoptimize down to // proxy get call. MOZ_ASSERT(!isSuper()); writer.proxyGetResult(objId, id); writer.returnFromIC(); @@ -1755,25 +1746,24 @@ AttachDecision GetPropIRGenerator::tryAt bool isLength = id.isAtom(cx_->names().length); bool isByteOffset = id.isAtom(cx_->names().byteOffset); if (!isLength && !isByteOffset && !id.isAtom(cx_->names().byteLength)) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); if (type != CanAttachNativeGetter) { return AttachDecision::NoAction; } - ShapeProperty prop = ShapeProperty(shape); - JSFunction& fun = holder->getGetter(prop)->as<JSFunction>(); + JSFunction& fun = holder->getGetter(*prop)->as<JSFunction>(); if (isLength) { if (!TypedArrayObject::isOriginalLengthGetter(fun.native())) { return AttachDecision::NoAction; } } else if (isByteOffset) { if (!TypedArrayObject::isOriginalByteOffsetGetter(fun.native())) { return AttachDecision::NoAction; } @@ -1783,17 +1773,17 @@ AttachDecision GetPropIRGenerator::tryAt } } auto* tarr = &obj->as<TypedArrayObject>(); maybeEmitIdGuard(id); // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. - EmitCallGetterResultGuards(writer, tarr, holder, shape, objId, mode_); + EmitCallGetterResultGuards(writer, tarr, holder, *prop, objId, mode_); if (isLength) { if (tarr->length() <= INT32_MAX) { writer.loadArrayBufferViewLengthInt32Result(objId); } else { writer.loadArrayBufferViewLengthDoubleResult(objId); } trackAttached("TypedArrayLength"); } else if (isByteOffset) { @@ -1839,39 +1829,38 @@ AttachDecision GetPropIRGenerator::tryAt } // byteOffset and byteLength both throw when the ArrayBuffer is detached. if (dv->hasDetachedBuffer()) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); if (type != CanAttachNativeGetter) { return AttachDecision::NoAction; } - ShapeProperty prop = ShapeProperty(shape); - auto& fun = holder->getGetter(prop)->as<JSFunction>(); + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); if (isByteOffset) { if (!DataViewObject::isOriginalByteOffsetGetter(fun.native())) { return AttachDecision::NoAction; } } else { if (!DataViewObject::isOriginalByteLengthGetter(fun.native())) { return AttachDecision::NoAction; } } maybeEmitIdGuard(id); // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. - EmitCallGetterResultGuards(writer, dv, holder, shape, objId, mode_); + EmitCallGetterResultGuards(writer, dv, holder, *prop, objId, mode_); writer.guardHasAttachedArrayBuffer(objId); if (isByteOffset) { if (dv->byteOffset() <= INT32_MAX) { writer.arrayBufferViewByteOffsetInt32Result(objId); } else { writer.arrayBufferViewByteOffsetDoubleResult(objId); } trackAttached("DataViewByteOffset"); @@ -1904,39 +1893,38 @@ AttachDecision GetPropIRGenerator::tryAt return AttachDecision::NoAction; } if (!id.isAtom(cx_->names().byteLength)) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); if (type != CanAttachNativeGetter) { return AttachDecision::NoAction; } - ShapeProperty prop = ShapeProperty(shape); - auto& fun = holder->getGetter(prop)->as<JSFunction>(); + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); if (buf->is<ArrayBufferObject>()) { if (!ArrayBufferObject::isOriginalByteLengthGetter(fun.native())) { return AttachDecision::NoAction; } } else { if (!SharedArrayBufferObject::isOriginalByteLengthGetter(fun.native())) { return AttachDecision::NoAction; } } maybeEmitIdGuard(id); // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. - EmitCallGetterResultGuards(writer, buf, holder, shape, objId, mode_); + EmitCallGetterResultGuards(writer, buf, holder, *prop, objId, mode_); if (buf->byteLength() <= INT32_MAX) { writer.loadArrayBufferByteLengthInt32Result(objId); } else { writer.loadArrayBufferByteLengthDoubleResult(objId); } writer.returnFromIC(); trackAttached("ArrayBufferMaybeSharedByteLength"); @@ -1956,34 +1944,33 @@ AttachDecision GetPropIRGenerator::tryAt } // Receiver should be the object. if (isSuper()) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, obj, id, &holder, &prop, pc_); if (type != CanAttachNativeGetter) { return AttachDecision::NoAction; } - ShapeProperty prop = ShapeProperty(shape); - auto& fun = holder->getGetter(prop)->as<JSFunction>(); + auto& fun = holder->getGetter(*prop)->as<JSFunction>(); JS::RegExpFlags flags = JS::RegExpFlag::NoFlags; if (!RegExpObject::isOriginalFlagGetter(fun.native(), &flags)) { return AttachDecision::NoAction; } maybeEmitIdGuard(id); // Emit all the normal guards for calling this native, but specialize // callNativeGetterResult. - EmitCallGetterResultGuards(writer, regExp, holder, shape, objId, mode_); + EmitCallGetterResultGuards(writer, regExp, holder, *prop, objId, mode_); writer.regExpFlagResult(objId, flags.value()); writer.returnFromIC(); trackAttached("RegExpFlag"); return AttachDecision::Attach; } @@ -2116,17 +2103,17 @@ AttachDecision GetPropIRGenerator::tryAt return AttachDecision::NoAction; } // Check for the specific namespace object. maybeEmitIdGuard(id); writer.guardSpecificObject(objId, ns); ObjOperandId envId = writer.loadObject(env); - EmitLoadSlotResult(writer, envId, env, shape); + EmitLoadSlotResult(writer, envId, env, ShapeProperty(shape)); writer.returnFromIC(); trackAttached("ModuleNamespace"); return AttachDecision::Attach; } AttachDecision GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, HandleId id) { @@ -2164,34 +2151,34 @@ AttachDecision GetPropIRGenerator::tryAt } JSObject* proto = cx_->global()->maybeGetPrototype(protoKey); if (!proto) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* shape = nullptr; + Maybe<ShapeProperty> prop; NativeGetPropCacheability type = - CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_); + CanAttachNativeGetProp(cx_, proto, id, &holder, &prop, pc_); switch (type) { case CanAttachNone: return AttachDecision::NoAction; case CanAttachReadSlot: { auto* nproto = &proto->as<NativeObject>(); if (val_.isNumber()) { writer.guardIsNumber(valId); } else { writer.guardNonDoubleType(valId, val_.type()); } maybeEmitIdGuard(id); ObjOperandId protoId = writer.loadObject(nproto); - EmitReadSlotResult(writer, nproto, holder, shape, protoId); + EmitReadSlotResult(writer, nproto, holder, prop, protoId); writer.returnFromIC(); trackAttached("PrimitiveSlot"); return AttachDecision::Attach; } case CanAttachScriptedGetter: case CanAttachNativeGetter: { auto* nproto = &proto->as<NativeObject>(); @@ -2199,17 +2186,17 @@ AttachDecision GetPropIRGenerator::tryAt if (val_.isNumber()) { writer.guardIsNumber(valId); } else { writer.guardNonDoubleType(valId, val_.type()); } maybeEmitIdGuard(id); ObjOperandId protoId = writer.loadObject(nproto); - EmitCallGetterResult(cx_, writer, nproto, holder, shape, protoId, valId, + EmitCallGetterResult(cx_, writer, nproto, holder, *prop, protoId, valId, mode_); trackAttached("PrimitiveGetter"); return AttachDecision::Attach; } } MOZ_CRASH("Bad NativeGetPropCacheability"); @@ -2736,23 +2723,22 @@ AttachDecision GetNameIRGenerator::tryAt trackAttached(IRGenerator::NotAttached); return AttachDecision::NoAction; } static bool CanAttachGlobalName(JSContext* cx, GlobalLexicalEnvironmentObject* globalLexical, JS::PropertyKey id, NativeObject** holder, - Shape** shape) { + Maybe<ShapeProperty>* prop) { // The property must be found, and it must be found as a normal data property. NativeObject* current = globalLexical; while (true) { - mozilla::Maybe<ShapeProperty> prop = current->lookup(cx, id); - if (prop.isSome()) { - *shape = prop->shapeDeprecated(); + *prop = current->lookup(cx, id); + if (prop->isSome()) { break; } if (current == globalLexical) { current = &globalLexical->global(); } else { // In the browser the global prototype chain should be immutable. if (!current->staticPrototypeIsImmutable()) { @@ -2776,43 +2762,43 @@ AttachDecision GetNameIRGenerator::tryAt HandleId id) { if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope()) { return AttachDecision::NoAction; } auto* globalLexical = &env_->as<GlobalLexicalEnvironmentObject>(); NativeObject* holder = nullptr; - Shape* shape = nullptr; - if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) { return AttachDecision::NoAction; } // The property must be found, and it must be found as a normal data property. - if (!shape->isDataProperty()) { + if (!prop->isDataProperty()) { return AttachDecision::NoAction; } // This might still be an uninitialized lexical. - if (holder->getSlot(shape->slot()).isMagic()) { + if (holder->getSlot(prop->slot()).isMagic()) { return AttachDecision::NoAction; } if (holder == globalLexical) { // There is no need to guard on the shape. Lexical bindings are // non-configurable, and this stub cannot be shared across globals. size_t dynamicSlotOffset = - holder->dynamicSlotIndex(shape->slot()) * sizeof(Value); + holder->dynamicSlotIndex(prop->slot()) * sizeof(Value); writer.loadDynamicSlotResult(objId, dynamicSlotOffset); } else { // Check the prototype chain from the global to the holder // prototype. Ignore the global lexical scope as it doesn't figure // into the prototype chain. We guard on the global lexical // scope's shape independently. - if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, shape)) { + if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, *prop)) { return AttachDecision::NoAction; } // Shape guard for global lexical. writer.guardShape(objId, globalLexical->lastProperty()); // Guard on the shape of the GlobalObject. ObjOperandId globalId = writer.loadEnclosingEnvironment(objId); @@ -2820,17 +2806,17 @@ AttachDecision GetNameIRGenerator::tryAt ObjOperandId holderId = globalId; if (holder != &globalLexical->global()) { // Shape guard holder. holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->lastProperty()); } - EmitLoadSlotResult(writer, holderId, holder, shape); + EmitLoadSlotResult(writer, holderId, holder, *prop); } writer.returnFromIC(); trackAttached("GlobalNameValue"); return AttachDecision::Attach; } @@ -2840,59 +2826,59 @@ AttachDecision GetNameIRGenerator::tryAt return AttachDecision::NoAction; } Handle<GlobalLexicalEnvironmentObject*> globalLexical = env_.as<GlobalLexicalEnvironmentObject>(); MOZ_ASSERT(globalLexical->isGlobal()); NativeObject* holder = nullptr; - Shape* shape = nullptr; - if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &prop)) { return AttachDecision::NoAction; } if (holder == globalLexical) { return AttachDecision::NoAction; } GlobalObject* global = &globalLexical->global(); - if (IsCacheableGetPropCall(global, holder, shape) != CanAttachNativeGetter) { + if (IsCacheableGetPropCall(global, holder, *prop) != CanAttachNativeGetter) { return AttachDecision::NoAction; } // Shape guard for global lexical. writer.guardShape(objId, globalLexical->lastProperty()); // Guard on the shape of the GlobalObject. ObjOperandId globalId = writer.loadEnclosingEnvironment(objId); writer.guardShape(globalId, global->lastProperty()); if (holder != global) { // Shape guard holder. ObjOperandId holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->lastProperty()); - EmitGuardGetterSetterSlot(writer, holder, shape, holderId, + EmitGuardGetterSetterSlot(writer, holder, *prop, holderId, /* holderIsConstant = */ true); } else { // Note: pass true for |holderIsConstant| because the holder must be the // current global object. - EmitGuardGetterSetterSlot(writer, holder, shape, globalId, + EmitGuardGetterSetterSlot(writer, holder, *prop, globalId, /* holderIsConstant = */ true); } - if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, global, holder, shape, + if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Getter, global, holder, *prop, mode_)) { // The global shape guard above ensures the instance JSClass is correct. - EmitCallDOMGetterResultNoGuards(writer, holder, shape, globalId); + EmitCallDOMGetterResultNoGuards(writer, holder, *prop, globalId); trackAttached("GlobalNameDOMGetter"); } else { ValOperandId receiverId = writer.boxObject(globalId); - EmitCallGetterResultNoGuards(cx_, writer, global, holder, shape, + EmitCallGetterResultNoGuards(cx_, writer, global, holder, *prop, receiverId); trackAttached("GlobalNameGetter"); } return AttachDecision::Attach; } static bool NeedEnvironmentShapeGuard(JSObject* envObj) { @@ -2943,17 +2929,17 @@ AttachDecision GetNameIRGenerator::tryAt if (prop.isSome()) { break; } env = env->enclosingEnvironment(); } holder = &env->as<NativeObject>(); - if (!IsCacheableGetPropReadSlot(holder, holder, prop->shapeDeprecated())) { + if (!IsCacheableGetPropReadSlot(holder, holder, *prop)) { return AttachDecision::NoAction; } if (holder->getSlot(prop->slot()).isMagic()) { return AttachDecision::NoAction; } ObjOperandId lastObjId = objId; env = env_; @@ -3620,100 +3606,102 @@ AttachDecision SetPropIRGenerator::tryAt rhsValId)); return AttachDecision::NoAction; } } return AttachDecision::NoAction; } static void EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, - NativeObject* nobj, Shape* shape, + NativeObject* nobj, ShapeProperty prop, ValOperandId rhsId) { - if (nobj->isFixedSlot(shape->slot())) { - size_t offset = NativeObject::getFixedSlotOffset(shape->slot()); + if (nobj->isFixedSlot(prop.slot())) { + size_t offset = NativeObject::getFixedSlotOffset(prop.slot()); writer.storeFixedSlot(objId, offset, rhsId); } else { - size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value); + size_t offset = nobj->dynamicSlotIndex(prop.slot()) * sizeof(Value); writer.storeDynamicSlot(objId, offset, rhsId); } writer.returnFromIC(); } -static Shape* LookupShapeForSetSlot(JSOp op, NativeObject* obj, jsid id) { +static Maybe<ShapeProperty> LookupShapeForSetSlot(JSOp op, NativeObject* obj, + jsid id) { Maybe<ShapeProperty> prop = obj->lookupPure(id); if (prop.isNothing() || !prop->isDataProperty() || !prop->writable()) { - return nullptr; + return mozilla::Nothing(); } // If this is an op like JSOp::InitElem / [[DefineOwnProperty]], the // property's attributes may have to be changed too, so make sure it's a // simple data property. if (IsPropertyInitOp(op) && (!prop->configurable() || !prop->enumerable())) { - return nullptr; - } - - return prop->shapeDeprecated(); + return mozilla::Nothing(); + } + + return prop; } static bool CanAttachNativeSetSlot(JSOp op, JSObject* obj, JS::PropertyKey id, - Shape** propShape) { + Maybe<ShapeProperty>* prop) { if (!obj->is<NativeObject>()) { return false; } - *propShape = LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id); - return *propShape; + *prop = LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id); + return prop->isSome(); } // There is no need to guard on the shape. Global lexical bindings are // non-configurable and can not be shadowed. -static bool IsGlobalLexicalSetGName(JSOp op, NativeObject* obj, Shape* shape) { +static bool IsGlobalLexicalSetGName(JSOp op, NativeObject* obj, + ShapeProperty prop) { // Ensure that the env can't change. if (op != JSOp::SetGName && op != JSOp::StrictSetGName) { return false; } if (!obj->is<GlobalLexicalEnvironmentObject>()) { return false; } // Uninitialized let bindings use a RuntimeLexicalErrorObject. - MOZ_ASSERT(!obj->getSlot(shape->slot()).isMagic()); - MOZ_ASSERT(shape->writable()); - MOZ_ASSERT(!shape->configurable()); + MOZ_ASSERT(!obj->getSlot(prop.slot()).isMagic()); + MOZ_ASSERT(prop.writable()); + MOZ_ASSERT(!prop.configurable()); return true; } AttachDecision SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId) { - Shape* propShape = nullptr; - if (!CanAttachNativeSetSlot(JSOp(*pc_), obj, id, &propShape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachNativeSetSlot(JSOp(*pc_), obj, id, &prop)) { return AttachDecision::NoAction; } // Don't attach a megamorphic store slot stub for ops like JSOp::InitElem. if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp && IsPropertySetOp(JSOp(*pc_))) { writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId); writer.returnFromIC(); trackAttached("MegamorphicNativeSlot"); return AttachDecision::Attach; } maybeEmitIdGuard(id); NativeObject* nobj = &obj->as<NativeObject>(); - if (!IsGlobalLexicalSetGName(JSOp(*pc_), nobj, propShape)) { + if (!IsGlobalLexicalSetGName(JSOp(*pc_), nobj, *prop)) { TestMatchingNativeReceiver(writer, nobj, objId); } - EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId); + EmitStoreSlotAndReturn(writer, objId, nobj, *prop, rhsId); trackAttached("NativeSlot"); return AttachDecision::Attach; } OperandId IRGenerator::emitNumericGuard(ValOperandId valId, Scalar::Type type) { switch (type) { case Scalar::Int8: @@ -3757,21 +3745,20 @@ void SetPropIRGenerator::trackAttached(c sp.valueProperty("base", lhsVal_); sp.valueProperty("property", idVal_); sp.valueProperty("value", rhsVal_); } #endif } static bool IsCacheableSetPropCallNative(NativeObject* obj, - NativeObject* holder, Shape* shape) { - MOZ_ASSERT(shape); + NativeObject* holder, + ShapeProperty prop) { MOZ_ASSERT(IsCacheableProtoChain(obj, holder)); - ShapeProperty prop = ShapeProperty(shape); if (!prop.isAccessorProperty()) { return false; } JSObject* setterObject = holder->getSetter(prop); if (!setterObject || !setterObject->is<JSFunction>()) { return false; } @@ -3788,25 +3775,24 @@ static bool IsCacheableSetPropCallNative if (setter.hasJitInfo() && !setter.jitInfo()->needsOuterizedThisObject()) { return true; } return !IsWindow(obj); } static bool IsCacheableSetPropCallScripted(NativeObject* obj, - NativeObject* holder, Shape* shape) { - MOZ_ASSERT(shape); + NativeObject* holder, + ShapeProperty prop) { MOZ_ASSERT(IsCacheableProtoChain(obj, holder)); if (IsWindow(obj)) { return false; } - ShapeProperty prop = ShapeProperty(shape); if (!prop.isAccessorProperty()) { return false; } JSObject* setterObject = holder->getSetter(prop); if (!setterObject || !setterObject->is<JSFunction>()) { return false; } @@ -3817,77 +3803,75 @@ static bool IsCacheableSetPropCallScript } // Scripted functions and natives with JIT entry can use the scripted path. return setter.hasJitEntry(); } static bool CanAttachSetter(JSContext* cx, jsbytecode* pc, JSObject* obj, JS::PropertyKey id, NativeObject** holder, - Shape** propShape) { + Maybe<ShapeProperty>* shapeProp) { // Don't attach a setter stub for ops like JSOp::InitElem. MOZ_ASSERT(IsPropertySetOp(JSOp(*pc))); PropertyResult prop; if (!LookupPropertyPure(cx, obj, id, holder, &prop)) { return false; } auto* nobj = &obj->as<NativeObject>(); if (!prop.isNativeProperty()) { return false; } - *propShape = prop.shapeProperty().shapeDeprecated(); - if (!IsCacheableSetPropCallScripted(nobj, *holder, *propShape) && - !IsCacheableSetPropCallNative(nobj, *holder, *propShape)) { + if (!IsCacheableSetPropCallScripted(nobj, *holder, prop.shapeProperty()) && + !IsCacheableSetPropCallNative(nobj, *holder, prop.shapeProperty())) { return false; } + *shapeProp = mozilla::Some(prop.shapeProperty()); return true; } static void EmitCallSetterNoGuards(JSContext* cx, CacheIRWriter& writer, NativeObject* obj, NativeObject* holder, - Shape* shape, ObjOperandId objId, + ShapeProperty prop, ObjOperandId objId, ValOperandId rhsId) { - ShapeProperty prop = ShapeProperty(shape); JSFunction* target = &holder->getSetter(prop)->as<JSFunction>(); bool sameRealm = cx->realm() == target->realm(); if (target->isNativeWithoutJitEntry()) { - MOZ_ASSERT(IsCacheableSetPropCallNative(obj, holder, shape)); + MOZ_ASSERT(IsCacheableSetPropCallNative(obj, holder, prop)); writer.callNativeSetter(objId, target, rhsId, sameRealm); writer.returnFromIC(); return; } - MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape)); + MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, prop)); writer.callScriptedSetter(objId, target, rhsId, sameRealm); writer.returnFromIC(); } static void EmitCallDOMSetterNoGuards(JSContext* cx, CacheIRWriter& writer, - NativeObject* holder, Shape* shape, + NativeObject* holder, ShapeProperty prop, ObjOperandId objId, ValOperandId rhsId) { - ShapeProperty prop = ShapeProperty(shape); JSFunction* setter = &holder->getSetter(prop)->as<JSFunction>(); MOZ_ASSERT(cx->realm() == setter->realm()); writer.callDOMSetter(objId, setter->jitInfo(), rhsId); writer.returnFromIC(); } AttachDecision SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId) { NativeObject* holder = nullptr; - Shape* propShape = nullptr; - if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &prop)) { return AttachDecision::NoAction; } auto* nobj = &obj->as<NativeObject>(); maybeEmitIdGuard(id); // Use the megamorphic guard if we're in megamorphic mode, except if |obj| // is a Window as GuardHasGetterSetter doesn't support this yet (Window may @@ -3897,36 +3881,35 @@ AttachDecision SetPropIRGenerator::tryAt if (nobj != holder) { GeneratePrototypeGuards(writer, nobj, holder, objId); // Guard on the holder's shape. ObjOperandId holderId = writer.loadObject(holder); TestMatchingHolder(writer, holder, holderId); - EmitGuardGetterSetterSlot(writer, holder, propShape, holderId, + EmitGuardGetterSetterSlot(writer, holder, *prop, holderId, /* holderIsConstant = */ true); } else { - EmitGuardGetterSetterSlot(writer, holder, propShape, objId); - } - } else { - ShapeProperty prop = ShapeProperty(propShape); - GetterSetter* gs = holder->getGetterSetter(prop); + EmitGuardGetterSetterSlot(writer, holder, *prop, objId); + } + } else { + GetterSetter* gs = holder->getGetterSetter(*prop); writer.guardHasGetterSetter(objId, id, gs); } - if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Setter, nobj, holder, propShape, + if (CanAttachDOMGetterSetter(cx_, JSJitInfo::Setter, nobj, holder, *prop, mode_)) { - EmitCallDOMSetterNoGuards(cx_, writer, holder, propShape, objId, rhsId); + EmitCallDOMSetterNoGuards(cx_, writer, holder, *prop, objId, rhsId); trackAttached("DOMSetter"); return AttachDecision::Attach; } - EmitCallSetterNoGuards(cx_, writer, nobj, holder, propShape, objId, rhsId); + EmitCallSetterNoGuards(cx_, writer, nobj, holder, *prop, objId, rhsId); trackAttached("Setter"); return AttachDecision::Attach; } AttachDecision SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id, @@ -4276,41 +4259,41 @@ AttachDecision SetPropIRGenerator::tryAt MOZ_ASSERT(IsCacheableDOMProxy(obj)); JSObject* proto = obj->staticPrototype(); if (!proto) { return AttachDecision::NoAction; } NativeObject* holder = nullptr; - Shape* propShape = nullptr; - if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &prop)) { return AttachDecision::NoAction; } auto* nproto = &proto->as<NativeObject>(); maybeEmitIdGuard(id); // Guard that our expando object hasn't started shadowing this property. TestMatchingProxyReceiver(writer, obj, objId); CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId); GeneratePrototypeGuards(writer, obj, holder, objId); // Guard on the holder of the property. ObjOperandId holderId = writer.loadObject(holder); TestMatchingHolder(writer, holder, holderId); - EmitGuardGetterSetterSlot(writer, holder, propShape, holderId, + EmitGuardGetterSetterSlot(writer, holder, *prop, holderId, /* holderIsConstant = */ true); // EmitCallSetterNoGuards expects |obj| to be the object the property is // on to do some checks. Since we actually looked at proto, and no extra // guards will be generated, we can just pass that instead. - EmitCallSetterNoGuards(cx_, writer, nproto, holder, propShape, objId, rhsId); + EmitCallSetterNoGuards(cx_, writer, nproto, holder, *prop, objId, rhsId); trackAttached("DOMProxyUnshadowed"); return AttachDecision::Attach; } AttachDecision SetPropIRGenerator::tryAttachDOMProxyExpando( Handle<ProxyObject*> obj, ObjOperandId objId, HandleId id, ValOperandId rhsId) { @@ -4324,45 +4307,44 @@ AttachDecision SetPropIRGenerator::tryAt MOZ_ASSERT(!expandoVal.isUndefined(), "How did a missing expando manage to shadow things?"); auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate()); MOZ_ASSERT(expandoAndGeneration); expandoObj = &expandoAndGeneration->expando.toObject(); } - Shape* propShape = nullptr; - if (CanAttachNativeSetSlot(JSOp(*pc_), expandoObj, id, &propShape)) { + Maybe<ShapeProperty> prop; + if (CanAttachNativeSetSlot(JSOp(*pc_), expandoObj, id, &prop)) { auto* nativeExpandoObj = &expandoObj->as<NativeObject>(); maybeEmitIdGuard(id); ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape( obj, objId, expandoVal, nativeExpandoObj); - EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, + EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, *prop, rhsId); trackAttached("DOMProxyExpandoSlot"); return AttachDecision::Attach; } NativeObject* holder = nullptr; - if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape)) { + if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &prop)) { auto* nativeExpandoObj = &expandoObj->as<NativeObject>(); // Call the setter. Note that we pass objId, the DOM proxy, as |this| // and not the expando object. maybeEmitIdGuard(id); ObjOperandId expandoObjId = guardDOMProxyExpandoObjectAndShape( obj, objId, expandoVal, nativeExpandoObj); MOZ_ASSERT(holder == nativeExpandoObj); - EmitGuardGetterSetterSlot(writer, nativeExpandoObj, propShape, - expandoObjId); + EmitGuardGetterSetterSlot(writer, nativeExpandoObj, *prop, expandoObjId); EmitCallSetterNoGuards(cx_, writer, nativeExpandoObj, nativeExpandoObj, - propShape, objId, rhsId); + *prop, objId, rhsId); trackAttached("DOMProxyExpandoSetter"); return AttachDecision::Attach; } return AttachDecision::NoAction; } AttachDecision SetPropIRGenerator::tryAttachProxy(HandleObject obj, @@ -4461,28 +4443,28 @@ AttachDecision SetPropIRGenerator::tryAt // cases. if (mode_ == ICState::Mode::Megamorphic) { return AttachDecision::NoAction; } // Now try to do the set on the Window (the current global). GlobalObject* windowObj = cx_->global(); - Shape* propShape = nullptr; - if (!CanAttachNativeSetSlot(JSOp(*pc_), windowObj, id, &propShape)) { + Maybe<ShapeProperty> prop; + if (!CanAttachNativeSetSlot(JSOp(*pc_), windowObj, id, &prop)) { return AttachDecision::NoAction; } maybeEmitIdGuard(id); ObjOperandId windowObjId = GuardAndLoadWindowProxyWindow(writer, objId, windowObj); writer.guardShape(windowObjId, windowObj->lastProperty()); - EmitStoreSlotAndReturn(writer, windowObjId, windowObj, propShape, rhsId); + EmitStoreSlotAndReturn(writer, windowObjId, windowObj, *prop, rhsId); trackAttached("WindowProxySlot"); return AttachDecision::Attach; } bool SetPropIRGenerator::canAttachAddSlotStub(HandleObject obj, HandleId id) { // Special-case JSFunction resolve hook to allow redefining the 'prototype' // property without triggering lazy expansion of property and object @@ -4596,34 +4578,33 @@ AttachDecision SetPropIRGenerator::tryAt return AttachDecision::NoAction; } if (!obj->is<NativeObject>()) { return AttachDecision::NoAction; } auto* nobj = &obj->as<NativeObject>(); - Shape* propShape = prop.shapeProperty().shapeDeprecated(); + ShapeProperty shapeProp = prop.shapeProperty(); NativeObject* holder = nobj; - MOZ_ASSERT(propShape); - // The property must be the last added property of the object. + Shape* propShape = shapeProp.shapeDeprecated(); MOZ_RELEASE_ASSERT(holder->lastProperty() == propShape); // Old shape should be parent of new shape. Object flag updates may make this // false even for simple data properties. It may be possible to support these // transitions in the future, but ignore now for simplicity. if (propShape->previous() != oldShape) { return AttachDecision::NoAction; } // Basic shape checks. - if (propShape->inDictionary() || !propShape->isDataProperty() || - !propShape->writable()) { + if (propShape->inDictionary() || !shapeProp.isDataProperty() || + !shapeProp.writable()) { return AttachDecision::NoAction; } ObjOperandId objId = writer.guardToObject(objValId); maybeEmitIdGuard(id); // Shape guard the object. writer.guardShape(objId, oldShape); @@ -4632,22 +4613,22 @@ AttachDecision SetPropIRGenerator::tryAt // function is a non-builtin constructor. See canAttachAddSlotStub. if (nobj->is<JSFunction>() && id.isAtom(cx_->names().prototype)) { MOZ_ASSERT(nobj->as<JSFunction>().isNonBuiltinConstructor()); writer.guardFunctionIsNonBuiltinCtor(objId); } ShapeGuardProtoChain(writer, nobj, objId); - if (holder->isFixedSlot(propShape->slot())) { - size_t offset = NativeObject::getFixedSlotOffset(propShape->slot()); + if (holder->isFixedSlot(shapeProp.slot())) { + size_t offset = NativeObject::getFixedSlotOffset(shapeProp.slot()); writer.addAndStoreFixedSlot(objId, offset, rhsValId, propShape); trackAttached("AddSlot"); } else { - size_t offset = holder->dynamicSlotIndex(propShape->slot()) * sizeof(Value); + size_t offset = holder->dynamicSlotIndex(shapeProp.slot()) * sizeof(Value); uint32_t numOldSlots = NativeObject::calculateDynamicSlots(oldShape); uint32_t numNewSlots = holder->numDynamicSlots(); if (numOldSlots == numNewSlots) { writer.addAndStoreDynamicSlot(objId, offset, rhsValId, propShape); trackAttached("AddSlot"); } else { MOZ_ASSERT(numNewSlots > numOldSlots); writer.allocateAndStoreDynamicSlot(objId, offset, rhsValId, propShape,