mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 15:43:20 +00:00
LibJS: Add inline caching for adding new own properties to objects
We already had IC support in PutById for the following cases: - Changing an existing own property - Calling a setter located in the prototype chain This was enough to speed up code where structurally identical objects (same shape) are processed in a loop: ```js const arr = [{ a: 1 }, { a: 2 }, { a: 3 }]; for (let obj of arr) { obj.a += 1; } ``` However, creating structurally identical objects in a loop was still slow: ```js for (let i = 0; i < 10_000_000; i++) { const o = {}; o.a = 1; o.b = 2; o.c = 3; } ``` This change addresses that by adding a new IC type that caches both the source and target shapes, allowing property additions to be fast-pathed by directly jumping to the shape that already includes the new property.
This commit is contained in:
parent
a54215c07d
commit
d45f8a3081
Notes:
github-actions[bot]
2025-09-17 10:45:37 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: d45f8a3081
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6204
21 changed files with 162 additions and 75 deletions
|
@ -28,6 +28,16 @@ namespace JS::Bytecode {
|
|||
struct PropertyLookupCache {
|
||||
static constexpr size_t max_number_of_shapes_to_remember = 4;
|
||||
struct Entry {
|
||||
enum class Type {
|
||||
Empty,
|
||||
AddOwnProperty,
|
||||
ChangeOwnProperty,
|
||||
GetOwnProperty,
|
||||
ChangePropertyInPrototypeChain,
|
||||
GetPropertyInPrototypeChain,
|
||||
};
|
||||
Type type { Type::Empty };
|
||||
WeakPtr<Shape> from_shape;
|
||||
WeakPtr<Shape> shape;
|
||||
Optional<u32> property_offset;
|
||||
WeakPtr<Object> prototype;
|
||||
|
|
|
@ -1023,7 +1023,7 @@ inline ThrowCompletionOr<Value> get_by_id(VM& vm, Optional<IdentifierTableIndex>
|
|||
}
|
||||
}
|
||||
|
||||
CacheablePropertyMetadata cacheable_metadata;
|
||||
CacheableGetPropertyMetadata cacheable_metadata;
|
||||
auto value = TRY(base_obj->internal_get(executable.get_identifier(property), this_value, &cacheable_metadata));
|
||||
|
||||
// If internal_get() caused object's shape change, we can no longer be sure
|
||||
|
@ -1037,11 +1037,11 @@ inline ThrowCompletionOr<Value> get_by_id(VM& vm, Optional<IdentifierTableIndex>
|
|||
cache.entries[0] = {};
|
||||
return cache.entries[0];
|
||||
};
|
||||
if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
|
||||
if (cacheable_metadata.type == CacheableGetPropertyMetadata::Type::GetOwnProperty) {
|
||||
auto& entry = get_cache_slot();
|
||||
entry.shape = shape;
|
||||
entry.property_offset = cacheable_metadata.property_offset.value();
|
||||
} else if (cacheable_metadata.type == CacheablePropertyMetadata::Type::InPrototypeChain) {
|
||||
} else if (cacheable_metadata.type == CacheableGetPropertyMetadata::Type::GetPropertyInPrototypeChain) {
|
||||
auto& entry = get_cache_slot();
|
||||
entry.shape = &base_obj->shape();
|
||||
entry.property_offset = cacheable_metadata.property_offset.value();
|
||||
|
@ -1192,9 +1192,9 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
|
|||
}
|
||||
|
||||
if (TRY(binding_object.has_property(identifier))) {
|
||||
CacheablePropertyMetadata cacheable_metadata;
|
||||
CacheableGetPropertyMetadata cacheable_metadata;
|
||||
auto value = TRY(binding_object.internal_get(identifier, js_undefined(), &cacheable_metadata));
|
||||
if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
|
||||
if (cacheable_metadata.type == CacheableGetPropertyMetadata::Type::GetOwnProperty) {
|
||||
cache.entries[0].shape = shape;
|
||||
cache.entries[0].property_offset = cacheable_metadata.property_offset.value();
|
||||
}
|
||||
|
@ -1236,10 +1236,16 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
break;
|
||||
}
|
||||
case Op::PropertyKind::KeyValue: {
|
||||
auto& shape = object->shape();
|
||||
auto this_value_object = MUST(this_value.to_object(vm));
|
||||
auto& from_shape = this_value_object->shape();
|
||||
if (caches) {
|
||||
for (auto& cache : caches->entries) {
|
||||
if (cache.prototype) {
|
||||
switch (cache.type) {
|
||||
case PropertyLookupCache::Entry::Type::Empty:
|
||||
break;
|
||||
case PropertyLookupCache::Entry::Type::ChangePropertyInPrototypeChain: {
|
||||
if (!cache.prototype)
|
||||
break;
|
||||
// OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it.
|
||||
bool can_use_cache = [&]() -> bool {
|
||||
if (&object->shape() != cache.shape)
|
||||
|
@ -1257,7 +1263,11 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
return {};
|
||||
}
|
||||
}
|
||||
} else if (cache.shape == &object->shape()) {
|
||||
break;
|
||||
}
|
||||
case PropertyLookupCache::Entry::Type::ChangeOwnProperty: {
|
||||
if (cache.shape != &object->shape())
|
||||
break;
|
||||
auto value_in_object = object->get_direct(cache.property_offset.value());
|
||||
if (value_in_object.is_accessor()) {
|
||||
TRY(call(vm, value_in_object.as_accessor().setter(), this_value, value));
|
||||
|
@ -1266,32 +1276,74 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
}
|
||||
return {};
|
||||
}
|
||||
case PropertyLookupCache::Entry::Type::AddOwnProperty: {
|
||||
// OPTIMIZATION: If the object's shape is the same as the one cached before adding the new property, we can
|
||||
// reuse the resulting shape from the cache.
|
||||
if (cache.from_shape != &object->shape())
|
||||
break;
|
||||
if (!cache.shape)
|
||||
break;
|
||||
// The cache is invalid if the prototype chain has been mutated, since such a mutation could have added a setter for the property.
|
||||
if (cache.prototype_chain_validity && !cache.prototype_chain_validity->is_valid())
|
||||
break;
|
||||
object->unsafe_set_shape(*cache.shape);
|
||||
object->put_direct(*cache.property_offset, value);
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CacheablePropertyMetadata cacheable_metadata;
|
||||
CacheableSetPropertyMetadata cacheable_metadata;
|
||||
bool succeeded = TRY(object->internal_set(name, value, this_value, &cacheable_metadata));
|
||||
|
||||
auto get_cache_slot = [&] -> PropertyLookupCache::Entry& {
|
||||
for (size_t i = caches->entries.size() - 1; i >= 1; --i) {
|
||||
caches->entries[i] = caches->entries[i - 1];
|
||||
}
|
||||
caches->entries[0] = {};
|
||||
return caches->entries[0];
|
||||
};
|
||||
|
||||
if (succeeded && caches && cacheable_metadata.type == CacheableSetPropertyMetadata::Type::AddOwnProperty) {
|
||||
auto& cache = get_cache_slot();
|
||||
cache.type = PropertyLookupCache::Entry::Type::AddOwnProperty;
|
||||
cache.from_shape = from_shape;
|
||||
cache.property_offset = cacheable_metadata.property_offset.value();
|
||||
cache.shape = &object->shape();
|
||||
if (cacheable_metadata.prototype) {
|
||||
cache.prototype_chain_validity = *cacheable_metadata.prototype->shape().prototype_chain_validity();
|
||||
}
|
||||
}
|
||||
|
||||
// If internal_set() caused object's shape change, we can no longer be sure
|
||||
// that collected metadata is valid, e.g. if setter in prototype chain added
|
||||
// property with the same name into the object itself.
|
||||
if (succeeded && caches && &shape == &object->shape()) {
|
||||
auto get_cache_slot = [&] -> PropertyLookupCache::Entry& {
|
||||
for (size_t i = caches->entries.size() - 1; i >= 1; --i) {
|
||||
caches->entries[i] = caches->entries[i - 1];
|
||||
}
|
||||
caches->entries[0] = {};
|
||||
return caches->entries[0];
|
||||
};
|
||||
if (succeeded && caches && &from_shape == &object->shape()) {
|
||||
auto& cache = get_cache_slot();
|
||||
if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
|
||||
switch (cacheable_metadata.type) {
|
||||
case CacheableSetPropertyMetadata::Type::AddOwnProperty:
|
||||
// Something went wrong if we ended up here, because cacheable addition of a new property should've changed the shape.
|
||||
VERIFY_NOT_REACHED();
|
||||
break;
|
||||
case CacheableSetPropertyMetadata::Type::ChangeOwnProperty:
|
||||
cache.type = PropertyLookupCache::Entry::Type::ChangeOwnProperty;
|
||||
cache.shape = object->shape();
|
||||
cache.property_offset = cacheable_metadata.property_offset.value();
|
||||
} else if (cacheable_metadata.type == CacheablePropertyMetadata::Type::InPrototypeChain) {
|
||||
break;
|
||||
case CacheableSetPropertyMetadata::Type::ChangePropertyInPrototypeChain:
|
||||
cache.type = PropertyLookupCache::Entry::Type::ChangePropertyInPrototypeChain;
|
||||
cache.shape = object->shape();
|
||||
cache.property_offset = cacheable_metadata.property_offset.value();
|
||||
cache.prototype = *cacheable_metadata.prototype;
|
||||
cache.prototype_chain_validity = *cacheable_metadata.prototype->shape().prototype_chain_validity();
|
||||
break;
|
||||
case CacheableSetPropertyMetadata::Type::NotCacheable:
|
||||
break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2366,7 +2418,7 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
|
|||
}
|
||||
|
||||
if (TRY(binding_object.has_property(identifier))) {
|
||||
CacheablePropertyMetadata cacheable_metadata;
|
||||
CacheableSetPropertyMetadata cacheable_metadata;
|
||||
auto success = TRY(binding_object.internal_set(identifier, src, &binding_object, &cacheable_metadata));
|
||||
if (!success && vm.in_strict_mode()) {
|
||||
// Note: Nothing like this in the spec, this is here to produce nicer errors instead of the generic one thrown by Object::set().
|
||||
|
@ -2380,7 +2432,7 @@ ThrowCompletionOr<void> SetGlobal::execute_impl(Bytecode::Interpreter& interpret
|
|||
}
|
||||
return vm.throw_completion<TypeError>(ErrorType::ObjectSetReturnedFalse);
|
||||
}
|
||||
if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) {
|
||||
if (cacheable_metadata.type == CacheableSetPropertyMetadata::Type::ChangeOwnProperty) {
|
||||
cache.entries[0].shape = shape;
|
||||
cache.entries[0].property_offset = cacheable_metadata.property_offset.value();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ bool ArgumentsObject::parameter_map_has(PropertyKey const& key) const
|
|||
}
|
||||
|
||||
// 10.4.4.3 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver
|
||||
ThrowCompletionOr<Value> ArgumentsObject::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
ThrowCompletionOr<Value> ArgumentsObject::internal_get(PropertyKey const& property_key, Value receiver, CacheableGetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
{
|
||||
// 1. Let map be args.[[ParameterMap]].
|
||||
// 2. Let isMapped be ! HasOwnProperty(map, P).
|
||||
|
@ -56,7 +56,7 @@ ThrowCompletionOr<Value> ArgumentsObject::internal_get(PropertyKey const& proper
|
|||
}
|
||||
|
||||
// 10.4.4.4 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver
|
||||
ThrowCompletionOr<bool> ArgumentsObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase)
|
||||
ThrowCompletionOr<bool> ArgumentsObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase)
|
||||
{
|
||||
bool is_mapped = false;
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
|
||||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor&, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheableGetPropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
|
||||
|
||||
void set_mapped_names(Vector<Utf16FlyString> mapped_names) { m_mapped_names = move(mapped_names); }
|
||||
|
|
|
@ -326,7 +326,7 @@ bool Array::default_prototype_chain_intact() const
|
|||
return true;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<bool> Array::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
ThrowCompletionOr<bool> Array::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheableSetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
virtual ~Array() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override final;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor&, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override final;
|
||||
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override final;
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
|
||||
|
|
|
@ -136,7 +136,7 @@ ThrowCompletionOr<bool> ModuleNamespaceObject::internal_has_property(PropertyKey
|
|||
}
|
||||
|
||||
// 10.4.6.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver
|
||||
ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const& property_key, Value receiver, CacheableGetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
@ -183,7 +183,7 @@ ThrowCompletionOr<Value> ModuleNamespaceObject::internal_get(PropertyKey const&
|
|||
}
|
||||
|
||||
// 10.4.6.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver
|
||||
ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set(PropertyKey const&, Value, Value, CacheablePropertyMetadata*, PropertyLookupPhase)
|
||||
ThrowCompletionOr<bool> ModuleNamespaceObject::internal_set(PropertyKey const&, Value, Value, CacheableSetPropertyMetadata*, PropertyLookupPhase)
|
||||
{
|
||||
// 1. Return false.
|
||||
return false;
|
||||
|
|
|
@ -26,8 +26,8 @@ public:
|
|||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor&, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheableGetPropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
|
||||
virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const override;
|
||||
virtual void initialize(Realm&) override;
|
||||
|
|
|
@ -148,7 +148,7 @@ ThrowCompletionOr<void> Object::set(PropertyKey const& property_key, Value value
|
|||
}
|
||||
|
||||
// 7.3.5 CreateDataProperty ( O, P, V ), https://tc39.es/ecma262/#sec-createdataproperty
|
||||
ThrowCompletionOr<bool> Object::create_data_property(PropertyKey const& property_key, Value value)
|
||||
ThrowCompletionOr<bool> Object::create_data_property(PropertyKey const& property_key, Value value, Optional<u32>* new_property_offset)
|
||||
{
|
||||
// 1. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
|
||||
auto new_descriptor = PropertyDescriptor {
|
||||
|
@ -159,7 +159,10 @@ ThrowCompletionOr<bool> Object::create_data_property(PropertyKey const& property
|
|||
};
|
||||
|
||||
// 2. Return ? O.[[DefineOwnProperty]](P, newDesc).
|
||||
return internal_define_own_property(property_key, new_descriptor);
|
||||
auto result = internal_define_own_property(property_key, new_descriptor);
|
||||
if (new_property_offset && new_descriptor.property_offset.has_value())
|
||||
*new_property_offset = new_descriptor.property_offset.value();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.3.6 CreateMethodProperty ( O, P, V ), https://tc39.es/ecma262/#sec-createmethodproperty
|
||||
|
@ -890,7 +893,7 @@ ThrowCompletionOr<bool> Object::internal_has_property(PropertyKey const& propert
|
|||
|
||||
// 10.1.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver
|
||||
// 10.1.8.1 OrdinaryGet ( O, P, Receiver ), https://tc39.es/ecma262/#sec-ordinaryget
|
||||
ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, Value receiver, CacheableGetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
{
|
||||
VERIFY(!receiver.is_special_empty_value());
|
||||
|
||||
|
@ -917,16 +920,16 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
|
|||
if (!cacheable_metadata || !descriptor->property_offset.has_value() || !shape().is_cacheable())
|
||||
return;
|
||||
if (phase == PropertyLookupPhase::OwnProperty) {
|
||||
*cacheable_metadata = CacheablePropertyMetadata {
|
||||
.type = CacheablePropertyMetadata::Type::OwnProperty,
|
||||
*cacheable_metadata = CacheableGetPropertyMetadata {
|
||||
.type = CacheableGetPropertyMetadata::Type::GetOwnProperty,
|
||||
.property_offset = descriptor->property_offset.value(),
|
||||
.prototype = nullptr,
|
||||
};
|
||||
} else if (phase == PropertyLookupPhase::PrototypeChain) {
|
||||
VERIFY(shape().is_prototype_shape());
|
||||
VERIFY(shape().prototype_chain_validity()->is_valid());
|
||||
*cacheable_metadata = CacheablePropertyMetadata {
|
||||
.type = CacheablePropertyMetadata::Type::InPrototypeChain,
|
||||
*cacheable_metadata = CacheableGetPropertyMetadata {
|
||||
.type = CacheableGetPropertyMetadata::Type::GetPropertyInPrototypeChain,
|
||||
.property_offset = descriptor->property_offset.value(),
|
||||
.prototype = this,
|
||||
};
|
||||
|
@ -957,7 +960,7 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
|
|||
|
||||
// 10.1.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
|
||||
// 10.1.9.1 OrdinarySet ( O, P, V, Receiver ), https://tc39.es/ecma262/#sec-ordinaryset
|
||||
ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheableSetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
VERIFY(!value.is_special_empty_value());
|
||||
VERIFY(!receiver.is_special_empty_value());
|
||||
|
@ -970,7 +973,7 @@ ThrowCompletionOr<bool> Object::internal_set(PropertyKey const& property_key, Va
|
|||
}
|
||||
|
||||
// 10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ), https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor
|
||||
ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional<PropertyDescriptor> own_descriptor, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey const& property_key, Value value, Value receiver, Optional<PropertyDescriptor> own_descriptor, CacheableSetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
VERIFY(!value.is_special_empty_value());
|
||||
VERIFY(!receiver.is_special_empty_value());
|
||||
|
@ -999,21 +1002,21 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
|
|||
}
|
||||
}
|
||||
|
||||
auto update_inline_cache = [&] {
|
||||
auto update_inline_cache_for_property_change = [&] {
|
||||
// Non-standard: If the caller has requested cacheable metadata and the property is an own property, fill it in.
|
||||
if (!cacheable_metadata || !own_descriptor->property_offset.has_value() || !shape().is_cacheable())
|
||||
return;
|
||||
if (phase == PropertyLookupPhase::OwnProperty) {
|
||||
*cacheable_metadata = CacheablePropertyMetadata {
|
||||
.type = CacheablePropertyMetadata::Type::OwnProperty,
|
||||
*cacheable_metadata = CacheableSetPropertyMetadata {
|
||||
.type = CacheableSetPropertyMetadata::Type::ChangeOwnProperty,
|
||||
.property_offset = own_descriptor->property_offset.value(),
|
||||
.prototype = nullptr,
|
||||
};
|
||||
} else if (phase == PropertyLookupPhase::PrototypeChain) {
|
||||
VERIFY(shape().is_prototype_shape());
|
||||
VERIFY(shape().prototype_chain_validity()->is_valid());
|
||||
*cacheable_metadata = CacheablePropertyMetadata {
|
||||
.type = CacheablePropertyMetadata::Type::InPrototypeChain,
|
||||
*cacheable_metadata = CacheableSetPropertyMetadata {
|
||||
.type = CacheableSetPropertyMetadata::Type::ChangePropertyInPrototypeChain,
|
||||
.property_offset = own_descriptor->property_offset.value(),
|
||||
.prototype = this,
|
||||
};
|
||||
|
@ -1051,7 +1054,7 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
|
|||
// NOTE: We don't cache non-setter properties in the prototype chain, as that's a weird
|
||||
// use-case, and doesn't seem like something in need of optimization.
|
||||
if (phase == PropertyLookupPhase::OwnProperty)
|
||||
update_inline_cache();
|
||||
update_inline_cache_for_property_change();
|
||||
|
||||
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
|
||||
return TRY(receiver_object.internal_define_own_property(property_key, value_descriptor, &existing_descriptor));
|
||||
|
@ -1062,7 +1065,18 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
|
|||
VERIFY(!receiver_object.storage_has(property_key));
|
||||
|
||||
// ii. Return ? CreateDataProperty(Receiver, P, V).
|
||||
return TRY(receiver_object.create_data_property(property_key, value));
|
||||
Optional<u32> new_property_offset;
|
||||
auto result = TRY(receiver_object.create_data_property(property_key, value, &new_property_offset));
|
||||
auto& receiver_shape = receiver_object.shape();
|
||||
if (cacheable_metadata && new_property_offset.has_value() && !receiver_shape.is_dictionary() && receiver_shape.is_cacheable()) {
|
||||
VERIFY(!property_key.is_number());
|
||||
*cacheable_metadata = CacheableSetPropertyMetadata {
|
||||
.type = CacheableSetPropertyMetadata::Type::AddOwnProperty,
|
||||
.property_offset = *new_property_offset,
|
||||
.prototype = receiver_object.prototype(),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1076,7 +1090,7 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
|
|||
if (!setter)
|
||||
return false;
|
||||
|
||||
update_inline_cache();
|
||||
update_inline_cache_for_property_change();
|
||||
|
||||
// 6. Perform ? Call(setter, Receiver, « V »).
|
||||
(void)TRY(call(vm, *setter, receiver, value));
|
||||
|
|
|
@ -41,11 +41,23 @@ struct PrivateElement {
|
|||
|
||||
// Non-standard: This is information optionally returned by object property access functions.
|
||||
// It can be used to implement inline caches for property lookup.
|
||||
struct CacheablePropertyMetadata {
|
||||
struct CacheableGetPropertyMetadata {
|
||||
enum class Type {
|
||||
NotCacheable,
|
||||
OwnProperty,
|
||||
InPrototypeChain,
|
||||
GetOwnProperty,
|
||||
GetPropertyInPrototypeChain,
|
||||
};
|
||||
Type type { Type::NotCacheable };
|
||||
Optional<u32> property_offset;
|
||||
GC::Ptr<Object const> prototype;
|
||||
};
|
||||
|
||||
struct CacheableSetPropertyMetadata {
|
||||
enum class Type {
|
||||
NotCacheable,
|
||||
AddOwnProperty,
|
||||
ChangeOwnProperty,
|
||||
ChangePropertyInPrototypeChain,
|
||||
};
|
||||
Type type { Type::NotCacheable };
|
||||
Optional<u32> property_offset;
|
||||
|
@ -110,7 +122,7 @@ public:
|
|||
|
||||
ThrowCompletionOr<Value> get(PropertyKey const&) const;
|
||||
ThrowCompletionOr<void> set(PropertyKey const&, Value, ShouldThrowExceptions);
|
||||
ThrowCompletionOr<bool> create_data_property(PropertyKey const&, Value);
|
||||
ThrowCompletionOr<bool> create_data_property(PropertyKey const&, Value, Optional<u32>* new_property_offset = nullptr);
|
||||
void create_method_property(PropertyKey const&, Value);
|
||||
ThrowCompletionOr<bool> create_data_property_or_throw(PropertyKey const&, Value);
|
||||
void create_non_enumerable_data_property_or_throw(PropertyKey const&, Value);
|
||||
|
@ -145,8 +157,8 @@ public:
|
|||
OwnProperty,
|
||||
PrototypeChain,
|
||||
};
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) const;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty);
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheableGetPropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) const;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheableSetPropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty);
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&);
|
||||
virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const;
|
||||
|
||||
|
@ -156,7 +168,7 @@ public:
|
|||
// might not hold when property access behaves differently.
|
||||
bool may_interfere_with_indexed_property_access() const { return m_may_interfere_with_indexed_property_access; }
|
||||
|
||||
ThrowCompletionOr<bool> ordinary_set_with_own_descriptor(PropertyKey const&, Value, Value, Optional<PropertyDescriptor>, CacheablePropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty);
|
||||
ThrowCompletionOr<bool> ordinary_set_with_own_descriptor(PropertyKey const&, Value, Value, Optional<PropertyDescriptor>, CacheableSetPropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty);
|
||||
|
||||
// 10.4.7 Immutable Prototype Exotic Objects, https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects
|
||||
|
||||
|
@ -237,6 +249,7 @@ public:
|
|||
|
||||
Shape& shape() { return *m_shape; }
|
||||
Shape const& shape() const { return *m_shape; }
|
||||
void unsafe_set_shape(Shape&);
|
||||
|
||||
void convert_to_prototype_if_needed();
|
||||
|
||||
|
@ -263,8 +276,6 @@ protected:
|
|||
Object(ConstructWithPrototypeTag, Object& prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No);
|
||||
explicit Object(Shape&, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No);
|
||||
|
||||
void unsafe_set_shape(Shape&);
|
||||
|
||||
// [[Extensible]]
|
||||
bool m_is_extensible { true };
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@ ThrowCompletionOr<bool> ProxyObject::internal_has_property(PropertyKey const& pr
|
|||
}
|
||||
|
||||
// 10.5.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
|
||||
ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) const
|
||||
ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_key, Value receiver, CacheableGetPropertyMetadata*, PropertyLookupPhase) const
|
||||
{
|
||||
LIMIT_PROXY_RECURSION_DEPTH();
|
||||
|
||||
|
@ -539,7 +539,7 @@ ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_k
|
|||
}
|
||||
|
||||
// 10.5.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
|
||||
ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase)
|
||||
ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase)
|
||||
{
|
||||
LIMIT_PROXY_RECURSION_DEPTH();
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ public:
|
|||
virtual ThrowCompletionOr<Optional<PropertyDescriptor>> internal_get_own_property(PropertyKey const&) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_define_own_property(PropertyKey const&, PropertyDescriptor&, Optional<PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual ThrowCompletionOr<bool> internal_has_property(PropertyKey const&) const override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const&, Value receiver, CacheableGetPropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const&, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual ThrowCompletionOr<bool> internal_delete(PropertyKey const&) override;
|
||||
virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const override;
|
||||
virtual ThrowCompletionOr<Value> internal_call(ExecutionContext&, Value this_argument) override;
|
||||
|
|
|
@ -325,7 +325,7 @@ public:
|
|||
}
|
||||
|
||||
// 10.4.5.5 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-typedarray-get
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
|
||||
virtual ThrowCompletionOr<Value> internal_get(PropertyKey const& property_key, Value receiver, CacheableGetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const override
|
||||
{
|
||||
VERIFY(!receiver.is_special_empty_value());
|
||||
|
||||
|
@ -350,7 +350,7 @@ public:
|
|||
}
|
||||
|
||||
// 10.4.5.6 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override
|
||||
virtual ThrowCompletionOr<bool> internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheableSetPropertyMetadata*, PropertyLookupPhase) override
|
||||
{
|
||||
VERIFY(!value.is_special_empty_value());
|
||||
VERIFY(!receiver.is_special_empty_value());
|
||||
|
|
|
@ -215,7 +215,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> PlatformObject::internal
|
|||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
|
||||
JS::ThrowCompletionOr<bool> PlatformObject::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* metadata, PropertyLookupPhase phase)
|
||||
JS::ThrowCompletionOr<bool> PlatformObject::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata* metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
if (!m_legacy_platform_object_flags.has_value() || m_legacy_platform_object_flags->has_global_interface_extended_attribute)
|
||||
return Base::internal_set(property_name, value, receiver, metadata, phase);
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
|
||||
// ^JS::Object
|
||||
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value, JS::Value, JS::CacheablePropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value, JS::Value, JS::CacheableSetPropertyMetadata* = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor&, Optional<JS::PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override;
|
||||
|
|
|
@ -659,7 +659,7 @@ JS::ThrowCompletionOr<bool> Location::internal_define_own_property(JS::PropertyK
|
|||
}
|
||||
|
||||
// 7.10.5.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-get
|
||||
JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheableGetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase) const
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
@ -672,7 +672,7 @@ JS::ThrowCompletionOr<JS::Value> Location::internal_get(JS::PropertyKey const& p
|
|||
}
|
||||
|
||||
// 7.10.5.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/history.html#location-set
|
||||
JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
JS::ThrowCompletionOr<bool> Location::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ public:
|
|||
virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override;
|
||||
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor&, Optional<JS::PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheableGetPropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
|
||||
virtual JS::ThrowCompletionOr<GC::RootVector<JS::Value>> internal_own_property_keys() const override;
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ JS::ThrowCompletionOr<bool> WindowProxy::internal_define_own_property(JS::Proper
|
|||
|
||||
// 7.4.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-get
|
||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-get
|
||||
JS::ThrowCompletionOr<JS::Value> WindowProxy::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const
|
||||
JS::ThrowCompletionOr<JS::Value> WindowProxy::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheableGetPropertyMetadata*, PropertyLookupPhase) const
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
@ -175,7 +175,7 @@ JS::ThrowCompletionOr<JS::Value> WindowProxy::internal_get(JS::PropertyKey const
|
|||
|
||||
// 7.4.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-set
|
||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-set
|
||||
JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase)
|
||||
JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata*, PropertyLookupPhase)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ public:
|
|||
virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override;
|
||||
virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const&) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor&, Optional<JS::PropertyDescriptor>* precomputed_get_own_property = nullptr) override;
|
||||
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual JS::ThrowCompletionOr<JS::Value> internal_get(JS::PropertyKey const&, JS::Value receiver, JS::CacheableGetPropertyMetadata*, PropertyLookupPhase) const override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const&, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata*, PropertyLookupPhase) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const&) override;
|
||||
virtual JS::ThrowCompletionOr<GC::RootVector<JS::Value>> internal_own_property_keys() const override;
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ void ObservableArray::set_on_delete_an_indexed_value_callback(DeleteAnIndexedVal
|
|||
m_on_delete_an_indexed_value = GC::create_function(heap(), move(callback));
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<bool> ObservableArray::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* metadata, PropertyLookupPhase phase)
|
||||
JS::ThrowCompletionOr<bool> ObservableArray::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata* metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
if (property_key.is_number() && m_on_set_an_indexed_value)
|
||||
TRY(Bindings::throw_dom_exception_if_needed(vm(), [&] { return m_on_set_an_indexed_value->function()(value); }));
|
||||
|
|
|
@ -20,7 +20,7 @@ class WEB_API ObservableArray final : public JS::Array {
|
|||
public:
|
||||
static GC::Ref<ObservableArray> create(JS::Realm& realm);
|
||||
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheablePropertyMetadata* metadata = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver, JS::CacheableSetPropertyMetadata* metadata = nullptr, PropertyLookupPhase = PropertyLookupPhase::OwnProperty) override;
|
||||
virtual JS::ThrowCompletionOr<bool> internal_delete(JS::PropertyKey const& property_key) override;
|
||||
|
||||
using SetAnIndexedValueCallbackFunction = Function<ExceptionOr<void>(JS::Value&)>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue