Backed out 4 changesets (bug 1717438) for causing bc failures in browser_ext_browserAction_context.js.

CLOSED TREE

Backed out changeset 13111b300e29 (bug 1717438)
Backed out changeset 95e913632f6a (bug 1717438)
Backed out changeset 516ec345e629 (bug 1717438)
Backed out changeset c40c92bd9100 (bug 1717438)
This commit is contained in:
Brindusan Cristian 2021-07-15 18:05:21 +03:00
parent fccb304b45
commit 4346ceecd3
10 changed files with 64 additions and 211 deletions

View File

@ -5065,19 +5065,6 @@ static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
}
#endif
static bool HasInvalidatedTeleporting(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1 || !args[0].isObject()) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Expected single object argument");
return false;
}
args.rval().setBoolean(args[0].toObject().hasInvalidatedTeleporting());
return true;
}
static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
DumpBacktrace(cx);
@ -8136,10 +8123,6 @@ JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
" Return the address of the shared storage of a SharedArrayBuffer."),
#endif
JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting, 1, 0,
"hasInvalidatedTeleporting(obj)",
" Return true if the shape teleporting optimization has been disabled for |obj|."),
JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
"evalReturningScope(scriptStr, [global])",
" Evaluate the script in a new scope and return the scope.\n"

View File

@ -1,144 +0,0 @@
// The shape teleporting optimization can be disabled for an object that's used
// as prototype when either it's involved in prototype changes or it had a property
// shadowed on another prototype object.
function changeProps(o) {
Object.assign(o, {x: 1, y: 2, z: 3});
o.foo = 4;
delete o.x;
}
function testProtoChange() {
var receiver = {};
var A = Object.create(null);
var B = Object.create(A);
// Change |receiver|'s proto: receiver => B => A => null
// Because |receiver| is not used as a prototype object, this doesn't affect
// teleporting.
Object.setPrototypeOf(receiver, B);
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(A), false);
assertEq(hasInvalidatedTeleporting(B), false);
// Change B's proto to C: receiver => B => C => null
// Because B is used as prototype object, both A and B invalidate teleporting.
var C = Object.create(null);
Object.setPrototypeOf(B, C);
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(A), true);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(C), false);
// Change B's proto a second time: receiver => B => D => null
// Now C has teleporting invalidated too.
var D = Object.create(null);
Object.setPrototypeOf(B, D);
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(A), true);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(C), true);
assertEq(hasInvalidatedTeleporting(D), false);
// Changing properties (without shadowing) must not affect teleporting state.
changeProps(C);
changeProps(D);
assertEq(hasInvalidatedTeleporting(C), true);
assertEq(hasInvalidatedTeleporting(D), false);
}
testProtoChange();
function testShadowingProp() {
// receiver => C => B => A => null
var A = Object.create(null);
var B = Object.create(A);
var C = Object.create(B);
var receiver = Object.create(C);
// Adding non-shadowing properties doesn't affect teleporting.
A.a = 1;
B.b = 1;
C.c = 1;
receiver.receiver = 1;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), false);
assertEq(hasInvalidatedTeleporting(A), false);
// Objects not used as prototype can shadow properties without affecting
// teleporting.
receiver.a = 1;
receiver.b = 2;
receiver.c = 3;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), false);
assertEq(hasInvalidatedTeleporting(A), false);
// Shadowing a property of B on C invalidates teleporting for B.
C.b = 1;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(A), false);
// Shadowing a property of A on C invalidates teleporting for A.
C.a = 2;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(A), true);
// Changing properties (without shadowing) must not affect teleporting state.
changeProps(C);
changeProps(B);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
}
testShadowingProp();
function testShadowingPropStopsAtFirst() {
// receiver => C => B{x,y} => A{x,y,z} => null
var A = Object.create(null);
A.x = 1;
A.y = 2;
A.z = 3;
var B = Object.create(A);
B.x = 1;
B.y = 2;
var C = Object.create(B);
var receiver = Object.create(C);
// Teleporting is supported.
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), false);
assertEq(hasInvalidatedTeleporting(A), false);
// Shadowing a property of B (and A) on C.
// This invalidates teleporting for B but not for A, because the search stops
// at B.
C.x = 1;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(A), false);
// "y" is similar.
C.y = 2;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(A), false);
// "z" is only defined on A, so now A is affected.
C.z = 3;
assertEq(hasInvalidatedTeleporting(receiver), false);
assertEq(hasInvalidatedTeleporting(C), false);
assertEq(hasInvalidatedTeleporting(B), true);
assertEq(hasInvalidatedTeleporting(A), true);
}
testShadowingPropStopsAtFirst();
// Ensure teleporting properties on Object.prototype is still possible.
assertEq(hasInvalidatedTeleporting(Object.prototype), false);

View File

@ -621,6 +621,24 @@ static void TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj,
writer.guardShapeForClass(objId, obj->shape());
}
static bool ProtoChainSupportsTeleporting(JSObject* obj, NativeObject* holder) {
// The receiver should already have been handled since its checks are always
// required.
MOZ_ASSERT(obj->isUsedAsPrototype());
// Prototype chain must have cacheable prototypes to ensure the cached
// holder is the current holder.
for (JSObject* tmp = obj; tmp != holder; tmp = tmp->staticPrototype()) {
if (tmp->hasUncacheableProto()) {
return false;
}
}
// The holder itself only gets reshaped by teleportation if it is not
// marked UncacheableProto. See: ReshapeForProtoMutation.
return !holder->hasUncacheableProto();
}
static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
NativeObject* holder, ObjOperandId objId) {
// Assuming target property is on |holder|, generate appropriate guards to
@ -647,24 +665,23 @@ static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
// "holder". To optimize this access we need to ensure that neither D nor C
// has since defined a shadowing property 'x'. Since C is a prototype that
// we assume is rarely mutated we would like to avoid checking each time if
// new properties are added. To do this we require that whenever C starts
// shadowing a property on its proto chain, we invalidate (and opt out of) the
// teleporting optimization by setting the InvalidatedTeleporting flag on the
// object we're shadowing, triggering a shape change of that object. As a
// result, checking the shape of D and B is sufficient. Note that we do not
// care if the shape or properties of A change since the lookup of 'x' will
// stop at B.
// new properties are added. To do this we require that everytime C is
// mutated that in addition to generating a new shape for itself, it will
// walk the proto chain and generate new shapes for those objects on the
// chain (B and A). As a result, checking the shape of D and B is
// sufficient. Note that we do not care if the shape or properties of A
// change since the lookup of 'x' will stop at B.
//
// The second condition we must verify is that the prototype chain was not
// mutated. The same mechanism as above is used. When the prototype link is
// changed, we generate a new shape for the object. If the object whose
// link we are mutating is itself a prototype, we regenerate shapes down
// the chain by setting the InvalidatedTeleporting flag on them. This means
// the same two shape checks as above are sufficient.
// the chain by setting the UncacheableProto flag on them. This means the same
// two shape checks as above are sufficient.
//
// Once the InvalidatedTeleporting flag is set, it means the shape will no
// longer be changed by ReshapeForProtoMutation and ReshapeForShadowedProp.
// In this case we can no longer apply the optimization.
// Once the UncacheableProto flag is set, it means the shape will no longer be
// updated by ReshapeForProtoMutation. If any shape from receiver to holder
// (inclusive) is UncacheableProto, we don't apply the optimization.
//
// See:
// - ReshapeForProtoMutation
@ -678,8 +695,8 @@ static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
JSObject* pobj = obj->staticPrototype();
MOZ_ASSERT(pobj->isUsedAsPrototype());
// If teleporting is supported for this holder, we are done.
if (!holder->hasInvalidatedTeleporting()) {
// If teleporting is supported for this prototype chain, we are done.
if (ProtoChainSupportsTeleporting(pobj, holder)) {
return;
}

View File

@ -212,8 +212,8 @@ inline bool JSObject::isBoundFunction() const {
return is<JSFunction>() && as<JSFunction>().isBoundFunction();
}
inline bool JSObject::hasInvalidatedTeleporting() const {
return hasFlag(js::ObjectFlag::InvalidatedTeleporting);
inline bool JSObject::hasUncacheableProto() const {
return hasFlag(js::ObjectFlag::UncacheableProto);
}
MOZ_ALWAYS_INLINE bool JSObject::maybeHasInterestingSymbolProperty() const {

View File

@ -1719,8 +1719,8 @@ static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
//
// (2) The object is marked IsUsedAsPrototype. This implies the object may be
// participating in shape teleporting. To invalidate JIT ICs depending on
// the proto chain being unchanged, set the InvalidatedTeleporting shape
// flag for this object and objects on its proto chain.
// the proto chain being unchanged, set the UncacheableProto shape flag
// for this object and objects on its proto chain.
//
// This flag disables future shape teleporting attempts, so next time this
// happens the loop below will be a no-op.
@ -1739,8 +1739,8 @@ static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
RootedObject pobj(cx, obj);
while (pobj && pobj->is<NativeObject>()) {
if (!pobj->hasInvalidatedTeleporting()) {
if (!JSObject::setInvalidatedTeleporting(cx, pobj)) {
if (!pobj->hasUncacheableProto()) {
if (!JSObject::setUncacheableProto(cx, pobj)) {
return false;
}
}
@ -3218,8 +3218,8 @@ void JSObject::dump(js::GenericPrinter& out) const {
if (obj->isUnqualifiedVarObj()) {
out.put(" unqualified_varobj");
}
if (obj->hasInvalidatedTeleporting()) {
out.put(" invalidated_teleporting");
if (obj->hasUncacheableProto()) {
out.put(" has_uncacheable_proto");
}
if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) {
out.put(" immutable_prototype");

View File

@ -227,30 +227,17 @@ class JSObject
// exist on the scope chain) are kept.
inline bool isUnqualifiedVarObj() const;
// Once the "invalidated teleporting" flag is set for an object, it is never
// cleared and it may cause the JITs to insert additional guards when
// accessing properties on this object. While the flag remains clear, the
// shape teleporting optimization can be used to avoid those extra checks.
//
// The flag is set on the object if either:
//
// * Its own proto was mutated or it was on the proto chain of an object that
// had its proto mutated.
//
// * It was on the proto chain of an object that started shadowing a property
// on this object.
//
// See:
// - ReshapeForProtoMutation
// - ReshapeForShadowedProp
// - ProtoChainSupportsTeleporting
inline bool hasInvalidatedTeleporting() const;
static bool setInvalidatedTeleporting(JSContext* cx, JS::HandleObject obj) {
// An object with an "uncacheable proto" is a prototype object that either had
// its own proto mutated or it was on the proto chain of an object that had
// its proto mutated. This is used to opt-out of the shape teleporting
// optimization. See: ReshapeForProtoMutation, ProtoChainSupportsTeleporting.
inline bool hasUncacheableProto() const;
static bool setUncacheableProto(JSContext* cx, JS::HandleObject obj) {
MOZ_ASSERT(obj->isUsedAsPrototype());
MOZ_ASSERT(obj->hasStaticPrototype(),
"teleporting as a concept is only applicable to static "
"uncacheability as a concept is only applicable to static "
"(not dynamically-computed) prototypes");
return setFlag(cx, obj, js::ObjectFlag::InvalidatedTeleporting);
return setFlag(cx, obj, js::ObjectFlag::UncacheableProto);
}
/*

View File

@ -1138,7 +1138,7 @@ static bool ReshapeForShadowedPropSlow(JSContext* cx, HandleNativeObject obj,
}
if (proto->as<NativeObject>().contains(cx, id)) {
return JSObject::setInvalidatedTeleporting(cx, proto);
return NativeObject::reshapeForShadowedProp(cx, proto.as<NativeObject>());
}
proto = proto->staticPrototype();
@ -1164,6 +1164,15 @@ static MOZ_ALWAYS_INLINE bool ReshapeForShadowedProp(JSContext* cx,
return ReshapeForShadowedPropSlow(cx, obj.as<NativeObject>(), id);
}
/* static */
bool NativeObject::reshapeForShadowedProp(JSContext* cx,
HandleNativeObject obj) {
if (!obj->inDictionaryMode()) {
return toDictionaryMode(cx, obj);
}
return generateNewDictionaryShape(cx, obj);
}
static bool ChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
HandleObject getter, HandleObject setter,
PropertyFlags flags, PropertyResult* existing) {

View File

@ -760,6 +760,9 @@ class NativeObject : public JSObject {
[[nodiscard]] static bool generateNewDictionaryShape(JSContext* cx,
HandleNativeObject obj);
[[nodiscard]] static bool reshapeForShadowedProp(JSContext* cx,
HandleNativeObject obj);
// The maximum number of slots in an object.
// |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
// int32_t (see slotsSizeMustNotOverflow).

View File

@ -24,12 +24,7 @@ enum class ObjectFlag : uint16_t {
HasInterestingSymbol = 1 << 3,
// (1 << 4) is unused.
FrozenElements = 1 << 5, // See ObjectElements::FROZEN comment.
// If set, the shape teleporting optimization can no longer be used for
// accessing properties on this object.
// See: JSObject::hasInvalidatedTeleporting, ProtoChainSupportsTeleporting.
InvalidatedTeleporting = 1 << 6,
UncacheableProto = 1 << 6,
ImmutablePrototype = 1 << 7,
// See JSObject::isQualifiedVarObj().

View File

@ -84,6 +84,9 @@
// - Removing a property other than the object's last property.
// - The object has many properties. See maybeConvertToDictionaryForAdd for
// the heuristics.
// - For prototype objects: when a shadowing property is added to an object
// with this object on its prototype chain. This is used to invalidate the
// shape teleporting optimization. See reshapeForShadowedProp.
//
// Dictionary shapes are unshared, private to a single object, and always have
// a DictionaryPropMap that's similarly unshared. Dictionary shape mutations