mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-28 21:26:22 +00:00
LibJS: Add fast path for Array.prototype.shift
Makes `MicroBench/array-prototype-shift.js` 100x faster on my machine. Progress on https://github.com/LadybirdBrowser/ladybird/issues/5725
This commit is contained in:
parent
7c29db6ab0
commit
4b3a87eb14
Notes:
github-actions[bot]
2025-08-08 16:11:20 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: 4b3a87eb14
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5774
3 changed files with 42 additions and 25 deletions
|
@ -299,35 +299,36 @@ ThrowCompletionOr<Optional<PropertyDescriptor>> Array::internal_get_own_property
|
|||
return Object::internal_get_own_property(property_key);
|
||||
}
|
||||
|
||||
bool Array::default_prototype_chain_intact() const
|
||||
{
|
||||
auto const& intrinsics = m_realm->intrinsics();
|
||||
auto const* array_prototype = shape().prototype();
|
||||
if (!array_prototype)
|
||||
return false;
|
||||
if (!array_prototype->indexed_properties().is_empty())
|
||||
return false;
|
||||
auto const& array_prototype_shape = shape().prototype()->shape();
|
||||
if (intrinsics.default_array_prototype_shape().ptr() != &array_prototype_shape)
|
||||
return false;
|
||||
|
||||
auto const* object_prototype = array_prototype_shape.prototype();
|
||||
if (!object_prototype)
|
||||
return false;
|
||||
if (!object_prototype->indexed_properties().is_empty())
|
||||
return false;
|
||||
auto const& object_prototype_shape = array_prototype_shape.prototype()->shape();
|
||||
if (intrinsics.default_object_prototype_shape().ptr() != &object_prototype_shape)
|
||||
return false;
|
||||
if (object_prototype_shape.prototype())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ThrowCompletionOr<bool> Array::internal_set(PropertyKey const& property_key, Value value, Value receiver, CacheablePropertyMetadata* cacheable_metadata, PropertyLookupPhase phase)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto default_prototype_chain_intact = [&] {
|
||||
auto const& intrinsics = m_realm->intrinsics();
|
||||
auto* array_prototype = shape().prototype();
|
||||
if (!array_prototype)
|
||||
return false;
|
||||
if (!array_prototype->indexed_properties().is_empty())
|
||||
return false;
|
||||
auto& array_prototype_shape = shape().prototype()->shape();
|
||||
if (intrinsics.default_array_prototype_shape().ptr() != &array_prototype_shape)
|
||||
return false;
|
||||
|
||||
auto* object_prototype = array_prototype_shape.prototype();
|
||||
if (!object_prototype)
|
||||
return false;
|
||||
if (!object_prototype->indexed_properties().is_empty())
|
||||
return false;
|
||||
auto& object_prototype_shape = array_prototype_shape.prototype()->shape();
|
||||
if (intrinsics.default_object_prototype_shape().ptr() != &object_prototype_shape)
|
||||
return false;
|
||||
if (object_prototype_shape.prototype())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
VERIFY(receiver.is_object());
|
||||
auto& receiver_object = receiver.as_object();
|
||||
|
||||
|
|
|
@ -57,8 +57,11 @@ public:
|
|||
|
||||
[[nodiscard]] bool length_is_writable() const { return m_length_writable; }
|
||||
|
||||
bool is_proxy_target() const { return m_is_proxy_target; }
|
||||
void set_is_proxy_target(bool is_proxy_target) { m_is_proxy_target = is_proxy_target; }
|
||||
|
||||
bool default_prototype_chain_intact() const;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1220,6 +1220,19 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::shift)
|
|||
TRY(this_object->set(vm.names.length, Value(0), Object::ShouldThrowExceptions::Yes));
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
// OPTIMIZATION: If this object is Array that:
|
||||
// - is not a proxy target, which means get/set/has/delete will not trap.
|
||||
// - has intact prototype chain, which means we don't have to worry about getters/setters potentially defined for holes.
|
||||
// - has simple storage type, which means all values have default attributes (if some elements have configurable=false, we cannot use fast path, because delete operation will fail).
|
||||
// then we could take a fast path by directly taking first element from indexed storage.
|
||||
if (auto* array = as_if<Array>(*this_object); array && !array->is_proxy_target() && array->default_prototype_chain_intact() && array->indexed_properties().storage()->is_simple_storage()) {
|
||||
auto first = array->indexed_properties().storage()->take_first().value;
|
||||
if (first.is_special_empty_value())
|
||||
return js_undefined();
|
||||
return first;
|
||||
}
|
||||
|
||||
auto first = TRY(this_object->get(0));
|
||||
for (size_t k = 1; k < length; ++k) {
|
||||
size_t from = k;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue