LibJS: Support instrinsic Object properties with deferred evaluation

For performance, it is desirable to defer evaluation of intrinsics that
are stored on the GlobalObject for every created Realm. To support this,
Object now maintains a global storage map to store lambdas that will
return the associated intrinsic when evaluated. Once accessed, the
instrinsic is moved from this global map to normal Object storage.

To prevent this flow from becoming observable, when a deferred intrinsic
is stored, we still place an empty object in the normal Object storage.
This is so we still create the metadata for the object, and in doing so,
can preserve insertion order of the Object storage. Otherwise, this will
be observable by way of Object.getOwnPropertyDescriptors.
This commit is contained in:
Timothy Flynn 2022-11-24 09:41:24 -05:00 committed by Andreas Kling
parent 4f08f2f581
commit 12f9f3d9ef
Notes: sideshowbarker 2024-07-17 04:03:45 +09:00
2 changed files with 53 additions and 1 deletions

View file

@ -24,6 +24,8 @@
namespace JS {
static HashMap<Object const*, HashMap<FlyString, Object::IntrinsicAccessor>> s_intrinsics;
// 10.1.12 OrdinaryObjectCreate ( proto [ , additionalInternalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinaryobjectcreate
Object* Object::create(Realm& realm, Object* prototype)
{
@ -67,6 +69,11 @@ Object::Object(Shape& shape)
m_storage.resize(shape.property_count());
}
Object::~Object()
{
s_intrinsics.remove(this);
}
void Object::initialize(Realm&)
{
}
@ -970,6 +977,23 @@ ThrowCompletionOr<bool> Object::set_immutable_prototype(Object* prototype)
return false;
}
static Optional<Object::IntrinsicAccessor> find_intrinsic_accessor(Object const* object, PropertyKey const& property_key)
{
if (!property_key.is_string())
return {};
auto intrinsics = s_intrinsics.find(object);
if (intrinsics == s_intrinsics.end())
return {};
auto accessor = intrinsics->value.find(property_key.as_string());
if (accessor == intrinsics->value.end())
return {};
intrinsics->value.remove(accessor);
return move(accessor->value);
}
Optional<ValueAndAttributes> Object::storage_get(PropertyKey const& property_key) const
{
VERIFY(property_key.is_valid());
@ -987,9 +1011,14 @@ Optional<ValueAndAttributes> Object::storage_get(PropertyKey const& property_key
auto metadata = shape().lookup(property_key.to_string_or_symbol());
if (!metadata.has_value())
return {};
if (auto accessor = find_intrinsic_accessor(this, property_key); accessor.has_value())
const_cast<Object&>(*this).m_storage[metadata->offset] = (*accessor)(shape().realm());
value = m_storage[metadata->offset];
attributes = metadata->attributes;
}
return ValueAndAttributes { .value = value, .attributes = attributes };
}
@ -1013,6 +1042,11 @@ void Object::storage_set(PropertyKey const& property_key, ValueAndAttributes con
return;
}
if (property_key.is_string()) {
if (auto intrinsics = s_intrinsics.find(this); intrinsics != s_intrinsics.end())
intrinsics->value.remove(property_key.as_string());
}
auto property_key_string_or_symbol = property_key.to_string_or_symbol();
auto metadata = shape().lookup(property_key_string_or_symbol);
@ -1050,6 +1084,11 @@ void Object::storage_delete(PropertyKey const& property_key)
if (property_key.is_number())
return m_indexed_properties.remove(property_key.as_number());
if (property_key.is_string()) {
if (auto intrinsics = s_intrinsics.find(this); intrinsics != s_intrinsics.end())
intrinsics->value.remove(property_key.as_string());
}
auto metadata = shape().lookup(property_key.to_string_or_symbol());
VERIFY(metadata.has_value());
@ -1098,6 +1137,16 @@ void Object::define_direct_accessor(PropertyKey const& property_key, FunctionObj
}
}
void Object::define_intrinsic_accessor(PropertyKey const& property_key, PropertyAttributes attributes, IntrinsicAccessor accessor)
{
VERIFY(property_key.is_string());
storage_set(property_key, { {}, attributes });
auto& intrinsics = s_intrinsics.ensure(this);
intrinsics.set(property_key.as_string(), move(accessor));
}
void Object::ensure_shape_is_unique()
{
if (shape().is_unique())

View file

@ -47,7 +47,7 @@ public:
static Object* create(Realm&, Object* prototype);
virtual void initialize(Realm&) override;
virtual ~Object() = default;
virtual ~Object();
enum class PropertyKind {
Key,
@ -151,6 +151,9 @@ public:
void define_direct_property(PropertyKey const& property_key, Value value, PropertyAttributes attributes) { storage_set(property_key, { value, attributes }); };
void define_direct_accessor(PropertyKey const&, FunctionObject* getter, FunctionObject* setter, PropertyAttributes attributes);
using IntrinsicAccessor = Value (*)(Realm&);
virtual void define_intrinsic_accessor(PropertyKey const&, PropertyAttributes attributes, IntrinsicAccessor accessor);
void define_native_function(Realm&, PropertyKey const&, SafeFunction<ThrowCompletionOr<Value>(VM&)>, i32 length, PropertyAttributes attributes);
void define_native_accessor(Realm&, PropertyKey const&, SafeFunction<ThrowCompletionOr<Value>(VM&)> getter, SafeFunction<ThrowCompletionOr<Value>(VM&)> setter, PropertyAttributes attributes);