From 93cd17db74a62026aa3f4aaca4c0e5c1bc96fb15 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Tue, 3 Jun 2025 19:09:33 +0200 Subject: [PATCH] LibJS: Add fast path `internal_has_property()` for Array If array has packed index property storage without holes, we could check if indexed property is present simple by checking if it's less than array's length. Makes the following program go 1.1x faster: ```js function f() { let array = []; for (let i = 0; i < 3_000; i++) { array.push(i); } for (let i = 0; i < 10_000; i++) { array.map(x => x * 2); } } f(); ``` --- Libraries/LibJS/Runtime/Array.cpp | 13 +++++++++++++ Libraries/LibJS/Runtime/Array.h | 1 + 2 files changed, 14 insertions(+) diff --git a/Libraries/LibJS/Runtime/Array.cpp b/Libraries/LibJS/Runtime/Array.cpp index 4cb10429406..f5ead14928f 100644 --- a/Libraries/LibJS/Runtime/Array.cpp +++ b/Libraries/LibJS/Runtime/Array.cpp @@ -393,6 +393,19 @@ ThrowCompletionOr Array::internal_define_own_property(PropertyKey const& p return Object::internal_define_own_property(property_key, property_descriptor, precomputed_get_own_property); } +// NON-STANDARD: Fast path to quickly check if an indexed property exists in array without holes +ThrowCompletionOr Array::internal_has_property(PropertyKey const& property_key) const +{ + auto const* storage = indexed_properties().storage(); + if (property_key.is_number() && !m_is_proxy_target && storage && storage->is_simple_storage()) { + auto const& simple_storage = static_cast(*storage); + if (!simple_storage.has_empty_elements() && property_key.as_number() < simple_storage.array_like_size()) { + return true; + } + } + return Object::internal_has_property(property_key); +} + // NON-STANDARD: Used to reject deletes to ephemeral (non-configurable) length property ThrowCompletionOr Array::internal_delete(PropertyKey const& property_key) { diff --git a/Libraries/LibJS/Runtime/Array.h b/Libraries/LibJS/Runtime/Array.h index cf936272709..c0997ba090c 100644 --- a/Libraries/LibJS/Runtime/Array.h +++ b/Libraries/LibJS/Runtime/Array.h @@ -50,6 +50,7 @@ public: virtual ThrowCompletionOr> internal_get_own_property(PropertyKey const&) const override final; virtual ThrowCompletionOr internal_set(PropertyKey const&, Value value, Value receiver, CacheablePropertyMetadata*, PropertyLookupPhase) override; virtual ThrowCompletionOr internal_define_own_property(PropertyKey const&, PropertyDescriptor const&, Optional* precomputed_get_own_property = nullptr) override final; + virtual ThrowCompletionOr internal_has_property(PropertyKey const&) const override final; virtual ThrowCompletionOr internal_delete(PropertyKey const&) override; virtual ThrowCompletionOr> internal_own_property_keys() const override final;