Bug 1696861 part 6 - Rename Delegate flag to IsUsedAsPrototype. r=tcampbell

Differential Revision: https://phabricator.services.mozilla.com/D107421
This commit is contained in:
Jan de Mooij 2021-03-09 14:28:24 +00:00
parent b5e10bfb9d
commit 0ddf1b7480
14 changed files with 70 additions and 51 deletions

View File

@ -151,7 +151,7 @@ bool InterpretObjLiteralObj(JSContext* cx, HandlePlainObject obj,
}
if (kind == PropertySetKind::UniqueNames) {
if (!AddDataPropertyNonDelegate(cx, obj, propId, propVal)) {
if (!AddDataPropertyNonPrototype(cx, obj, propId, propVal)) {
return false;
}
} else {

View File

@ -8048,7 +8048,7 @@ void GCRuntime::mergeRealms(Realm* source, Realm* target) {
if (GlobalObject::isOffThreadPrototypePlaceholder(obj)) {
JSObject* targetProto =
global->getPrototypeForOffThreadPlaceholder(obj);
MOZ_ASSERT(targetProto->isDelegate());
MOZ_ASSERT(targetProto->isUsedAsPrototype());
baseShape->setProtoForMergeRealms(TaggedProto(targetProto));
}
}

View File

@ -17,8 +17,8 @@ prettyprinters.clear_module_printers(__name__)
class JSObjectTypeCache(object):
def __init__(self, value, cache):
object_flag = gdb.lookup_type("js::ObjectFlag")
self.objectflag_Delegate = prettyprinters.enum_value(
object_flag, "js::ObjectFlag::Delegate"
self.objectflag_IsUsedAsPrototype = prettyprinters.enum_value(
object_flag, "js::ObjectFlag::IsUsedAsPrototype"
)
self.func_ptr_type = gdb.lookup_type("JSFunction").pointer()
self.class_NON_NATIVE = gdb.parse_and_eval("JSClass::NON_NATIVE")
@ -60,7 +60,7 @@ class JSObjectPtrOrRef(prettyprinters.Pointer):
return "[object {}]".format(class_name)
else:
flags = shape["objectFlags_"]["flags_"]
is_delegate = bool(flags & self.otc.objectflag_Delegate)
used_as_prototype = bool(flags & self.otc.objectflag_IsUsedAsPrototype)
name = None
if class_name == "Function":
function = self.value
@ -73,7 +73,7 @@ class JSObjectPtrOrRef(prettyprinters.Pointer):
return "[object {}{}]{}".format(
class_name,
" " + name if name else "",
" delegate" if is_delegate else "",
" used_as_prototype" if used_as_prototype else "",
)

View File

@ -13,7 +13,7 @@ run_fragment("JSObject.simple")
assert_pretty("glob", "(JSObject *) [object global]")
assert_pretty("plain", "(JSObject *) [object Object]")
assert_pretty("objectProto", "(JSObject *) [object Object] delegate")
assert_pretty("objectProto", "(JSObject *) [object Object] used_as_prototype")
assert_pretty("func", '(JSObject *) [object Function "dys"]')
assert_pretty("anon", "(JSObject *) [object Function <unnamed>]")
assert_pretty("funcPtr", '(JSFunction *) [object Function "formFollows"]')

View File

@ -645,7 +645,7 @@ static void TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj,
static bool ProtoChainSupportsTeleporting(JSObject* obj, JSObject* holder) {
// The receiver should already have been handled since its checks are always
// required.
MOZ_ASSERT(obj->isDelegate());
MOZ_ASSERT(obj->isUsedAsPrototype());
// Prototype chain must have cacheable prototypes to ensure the cached
// holder is the current holder.
@ -714,7 +714,7 @@ static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj,
// Receiver guards (see TestMatchingReceiver) ensure the receiver's proto is
// unchanged so peel off the receiver.
JSObject* pobj = obj->staticPrototype();
MOZ_ASSERT(pobj->isDelegate());
MOZ_ASSERT(pobj->isUsedAsPrototype());
// If teleporting is supported for this prototype chain, we are done.
if (ProtoChainSupportsTeleporting(pobj, holder)) {

View File

@ -1013,9 +1013,9 @@ static bool EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global,
return false;
}
// Mark the prototype as delegate here because we can't GC in mergeRealms.
// Set the used-as-prototype flag here because we can't GC in mergeRealms.
RootedObject proto(cx, &global->getPrototype(key).toObject());
return JSObject::setDelegate(cx, proto);
return JSObject::setIsUsedAsPrototype(cx, proto);
}
// Initialize all classes potentially created during parsing for use in parser

View File

@ -235,10 +235,6 @@ inline bool JSObject::isBoundFunction() const {
return is<JSFunction>() && as<JSFunction>().isBoundFunction();
}
inline bool JSObject::isDelegate() const {
return hasFlag(js::ObjectFlag::Delegate);
}
inline bool JSObject::hasUncacheableProto() const {
return hasFlag(js::ObjectFlag::UncacheableProto);
}

View File

@ -1523,9 +1523,9 @@ void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b,
// teleporting optimizations.
//
// See: ReshapeForProtoMutation, ReshapeForShadowedProp
MOZ_ASSERT_IF(a->is<NativeObject>() && a->isDelegate(),
MOZ_ASSERT_IF(a->is<NativeObject>() && a->isUsedAsPrototype(),
a->taggedProto() == TaggedProto());
MOZ_ASSERT_IF(b->is<NativeObject>() && b->isDelegate(),
MOZ_ASSERT_IF(b->is<NativeObject>() && b->isUsedAsPrototype(),
b->taggedProto() == TaggedProto());
bool aIsProxyWithInlineValues =
@ -1751,12 +1751,12 @@ static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
//
// There are two cases:
//
// (1) The object is not marked Delegate. This is the common case. Because
// shape implies proto, we rely on the caller changing the object's shape.
// The JIT guards on this object's shape or prototype so there's nothing
// we have to do here for objects on the proto chain.
// (1) The object is not marked IsUsedAsPrototype. This is the common case.
// Because shape implies proto, we rely on the caller changing the
// object's shape. The JIT guards on this object's shape or prototype so
// there's nothing we have to do here for objects on the proto chain.
//
// (2) The object is marked Delegate. This implies the object may be
// (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 UncacheableProto shape flag
// for this object and objects on its proto chain.
@ -1771,7 +1771,7 @@ static bool ReshapeForProtoMutation(JSContext* cx, HandleObject obj) {
// - GeneratePrototypeGuards
// - GeneratePrototypeHoleGuards
if (!obj->isDelegate()) {
if (!obj->isUsedAsPrototype()) {
return true;
}
@ -1799,7 +1799,7 @@ static bool SetProto(JSContext* cx, HandleObject obj,
if (proto.isObject()) {
RootedObject protoObj(cx, proto.toObject());
if (!JSObject::setDelegate(cx, protoObj)) {
if (!JSObject::setIsUsedAsPrototype(cx, protoObj)) {
return false;
}
}
@ -3209,15 +3209,27 @@ void JSObject::dump(js::GenericPrinter& out) const {
out.printf(" shape %p\n", shape);
out.put(" flags:");
if (obj->isDelegate()) out.put(" delegate");
if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible())
if (obj->isUsedAsPrototype()) {
out.put(" used_as_prototype");
}
if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) {
out.put(" not_extensible");
if (obj->maybeHasInterestingSymbolProperty())
}
if (obj->maybeHasInterestingSymbolProperty()) {
out.put(" maybe_has_interesting_symbol");
if (obj->isBoundFunction()) out.put(" bound_function");
if (obj->isQualifiedVarObj()) out.put(" varobj");
if (obj->isUnqualifiedVarObj()) out.put(" unqualified_varobj");
if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto");
}
if (obj->isBoundFunction()) {
out.put(" bound_function");
}
if (obj->isQualifiedVarObj()) {
out.put(" varobj");
}
if (obj->isUnqualifiedVarObj()) {
out.put(" unqualified_varobj");
}
if (obj->hasUncacheableProto()) {
out.put(" has_uncacheable_proto");
}
if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) {
out.put(" immutable_prototype");
}

View File

@ -162,12 +162,22 @@ class JSObject
static bool setProtoUnchecked(JSContext* cx, JS::HandleObject obj,
js::Handle<js::TaggedProto> proto);
// An object is a delegate if it is (or was) another object's prototype.
// Optimization heuristics will make use of this flag.
// An object is marked IsUsedAsPrototype if it is (or was) another object's
// prototype. Optimization heuristics will make use of this flag.
//
// This flag is only relevant for static prototypes. Proxy traps can return
// objects without this flag set.
//
// NOTE: it's important to call setIsUsedAsPrototype *after* initializing the
// object's properties, because that avoids unnecessary shadowing checks and
// reshaping.
//
// See: ReshapeForProtoMutation, ReshapeForShadowedProp
inline bool isDelegate() const;
static bool setDelegate(JSContext* cx, JS::HandleObject obj) {
return setFlag(cx, obj, js::ObjectFlag::Delegate, GENERATE_SHAPE);
bool isUsedAsPrototype() const {
return hasFlag(js::ObjectFlag::IsUsedAsPrototype);
}
static bool setIsUsedAsPrototype(JSContext* cx, JS::HandleObject obj) {
return setFlag(cx, obj, js::ObjectFlag::IsUsedAsPrototype, GENERATE_SHAPE);
}
inline bool isBoundFunction() const;
@ -202,12 +212,13 @@ class JSObject
// exist on the scope chain) are kept.
inline bool isUnqualifiedVarObj() const;
// An object with an "uncacheable proto" is a Delegate object that either had
// 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(),
"uncacheability as a concept is only applicable to static "
"(not dynamically-computed) prototypes");

View File

@ -853,11 +853,11 @@ inline bool IsPackedArray(JSObject* obj) {
return true;
}
MOZ_ALWAYS_INLINE bool AddDataPropertyNonDelegate(JSContext* cx,
HandlePlainObject obj,
HandleId id, HandleValue v) {
MOZ_ALWAYS_INLINE bool AddDataPropertyNonPrototype(JSContext* cx,
HandlePlainObject obj,
HandleId id, HandleValue v) {
MOZ_ASSERT(!JSID_IS_INT(id));
MOZ_ASSERT(!obj->isDelegate());
MOZ_ASSERT(!obj->isUsedAsPrototype());
// If we know this is a new property we can call addProperty instead of
// the slower putProperty.

View File

@ -1223,7 +1223,7 @@ static bool WouldDefinePastNonwritableLength(ArrayObject* arr, uint32_t index) {
static bool ReshapeForShadowedPropSlow(JSContext* cx, HandleNativeObject obj,
HandleId id) {
MOZ_ASSERT(obj->isDelegate());
MOZ_ASSERT(obj->isUsedAsPrototype());
// Lookups on integer ids cannot be cached through prototypes.
if (JSID_IS_INT(id)) {
@ -1257,7 +1257,7 @@ static MOZ_ALWAYS_INLINE bool ReshapeForShadowedProp(JSContext* cx,
// See also the 'Shape Teleporting Optimization' comment in jit/CacheIR.cpp.
// Inlined fast path for non-prototype/non-native objects.
if (!obj->isDelegate() || !obj->is<NativeObject>()) {
if (!obj->isUsedAsPrototype() || !obj->is<NativeObject>()) {
return true;
}
@ -2767,7 +2767,7 @@ bool js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target,
Handle<PlainObject*> excludedItems,
bool* optimized) {
MOZ_ASSERT(
!target->isDelegate(),
!target->isUsedAsPrototype(),
"CopyDataPropertiesNative should only be called during object literal "
"construction"
"which precludes that |target| is the prototype of any other object");
@ -2837,7 +2837,7 @@ bool js::CopyDataPropertiesNative(JSContext* cx, HandlePlainObject target,
MOZ_ASSERT(!target->contains(cx, key),
"didn't expect to find an existing property");
if (!AddDataPropertyNonDelegate(cx, target, key, value)) {
if (!AddDataPropertyNonPrototype(cx, target, key, value)) {
return false;
}
} else {

View File

@ -56,7 +56,7 @@ bool GlobalObject::splicePrototype(JSContext* cx, Handle<GlobalObject*> global,
if (proto.isObject()) {
RootedObject protoObj(cx, proto.toObject());
if (!JSObject::setDelegate(cx, protoObj)) {
if (!JSObject::setIsUsedAsPrototype(cx, protoObj)) {
return false;
}
}

View File

@ -1390,7 +1390,7 @@ bool JSObject::setFlag(JSContext* cx, HandleObject obj, ObjectFlag flag,
bool JSObject::setProtoUnchecked(JSContext* cx, HandleObject obj,
Handle<TaggedProto> proto) {
MOZ_ASSERT(cx->compartment() == obj->compartment());
MOZ_ASSERT_IF(proto.isObject(), proto.toObject()->isDelegate());
MOZ_ASSERT_IF(proto.isObject(), proto.toObject()->isUsedAsPrototype());
if (obj->shape()->proto() == proto) {
return true;
@ -1469,7 +1469,7 @@ inline BaseShape::BaseShape(const StackBaseShape& base)
MOZ_ASSERT_IF(proto().isObject(),
compartment() == proto().toObject()->compartment());
MOZ_ASSERT_IF(proto().isObject(), proto().toObject()->isDelegate());
MOZ_ASSERT_IF(proto().isObject(), proto().toObject()->isUsedAsPrototype());
// Windows may not appear on prototype chains.
MOZ_ASSERT_IF(proto().isObject(), !IsWindow(proto().toObject()));
@ -1985,9 +1985,9 @@ Shape* EmptyShape::getInitialShape(JSContext* cx, const JSClass* clasp,
MOZ_ASSERT_IF(proto.isObject(),
cx->isInsideCurrentCompartment(proto.toObject()));
if (proto.isObject() && !proto.toObject()->isDelegate()) {
if (proto.isObject() && !proto.toObject()->isUsedAsPrototype()) {
RootedObject protoObj(cx, proto.toObject());
if (!JSObject::setDelegate(cx, protoObj)) {
if (!JSObject::setIsUsedAsPrototype(cx, protoObj)) {
return nullptr;
}
proto = TaggedProto(protoObj);

View File

@ -646,7 +646,7 @@ struct StackBaseShape;
// If you add a new flag here, please add appropriate code to JSObject::dump to
// dump it as part of the object representation.
enum class ObjectFlag : uint16_t {
Delegate = 1 << 0,
IsUsedAsPrototype = 1 << 0,
NotExtensible = 1 << 1,
Indexed = 1 << 2,
HasInterestingSymbol = 1 << 3,