LibJS+LibWeb: Inline fast path for Value::to_object()

Adds inline implementation for the most common case when `Value` is
already an object.

1.47x improvement on the following benchmark:
```js
const o = {};
for (let i = 0; i < 10_000_000; i++) {
    o.a = 1;
    o.b = 2;
    o.c = 3;
}
```
This commit is contained in:
Aliaksandr Kalenik 2025-09-13 20:50:09 +02:00 committed by Alexander Kalenik
parent f5fd6338d5
commit 85e029b2e7
Notes: github-actions[bot] 2025-09-15 10:18:00 +00:00
16 changed files with 22 additions and 1 deletions

View file

@ -19,6 +19,7 @@
#include <LibJS/Runtime/PromiseCapability.h> #include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/PromiseConstructor.h> #include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -28,6 +28,7 @@
#include <LibJS/Runtime/PromiseCapability.h> #include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/PromiseConstructor.h> #include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -14,6 +14,7 @@
#include <LibJS/Runtime/ObjectConstructor.h> #include <LibJS/Runtime/ObjectConstructor.h>
#include <LibJS/Runtime/ProxyObject.h> #include <LibJS/Runtime/ProxyObject.h>
#include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -18,6 +18,7 @@
#include <LibJS/Runtime/RegExpObject.h> #include <LibJS/Runtime/RegExpObject.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -16,6 +16,7 @@
#include <LibJS/Runtime/PromiseCapability.h> #include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/PromiseConstructor.h> #include <LibJS/Runtime/PromiseConstructor.h>
#include <LibJS/Runtime/PromiseResolvingElementFunctions.h> #include <LibJS/Runtime/PromiseResolvingElementFunctions.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -11,6 +11,7 @@
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -8,6 +8,7 @@
#include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Reference.h> #include <LibJS/Runtime/Reference.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -8,6 +8,7 @@
#include <LibJS/Runtime/Iterator.h> #include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/TypedArrayConstructor.h> #include <LibJS/Runtime/TypedArrayConstructor.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS { namespace JS {

View file

@ -585,7 +585,7 @@ ThrowCompletionOr<Value> Value::to_primitive_slow_case(VM& vm, PreferredType pre
} }
// 7.1.18 ToObject ( argument ), https://tc39.es/ecma262/#sec-toobject // 7.1.18 ToObject ( argument ), https://tc39.es/ecma262/#sec-toobject
ThrowCompletionOr<GC::Ref<Object>> Value::to_object(VM& vm) const ThrowCompletionOr<GC::Ref<Object>> Value::to_object_slow(VM& vm) const
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
VERIFY(!is_special_empty_value()); VERIFY(!is_special_empty_value());

View file

@ -355,6 +355,7 @@ public:
ThrowCompletionOr<GC::Ref<PrimitiveString>> to_primitive_string(VM&); ThrowCompletionOr<GC::Ref<PrimitiveString>> to_primitive_string(VM&);
ThrowCompletionOr<Value> to_primitive(VM&, PreferredType preferred_type = PreferredType::Default) const; ThrowCompletionOr<Value> to_primitive(VM&, PreferredType preferred_type = PreferredType::Default) const;
ThrowCompletionOr<GC::Ref<Object>> to_object(VM&) const; ThrowCompletionOr<GC::Ref<Object>> to_object(VM&) const;
ThrowCompletionOr<GC::Ref<Object>> to_object_slow(VM&) const;
ThrowCompletionOr<Value> to_numeric(VM&) const; ThrowCompletionOr<Value> to_numeric(VM&) const;
ThrowCompletionOr<Value> to_number(VM&) const; ThrowCompletionOr<Value> to_number(VM&) const;
ThrowCompletionOr<GC::Ref<BigInt>> to_bigint(VM&) const; ThrowCompletionOr<GC::Ref<BigInt>> to_bigint(VM&) const;

View file

@ -48,4 +48,11 @@ inline ThrowCompletionOr<Value> Value::to_primitive(VM& vm, PreferredType prefer
return to_primitive_slow_case(vm, preferred_type); return to_primitive_slow_case(vm, preferred_type);
} }
inline ThrowCompletionOr<GC::Ref<Object>> Value::to_object(VM& vm) const
{
if (is_object())
return const_cast<Object&>(as_object());
return to_object_slow(vm);
}
} }

View file

@ -7,6 +7,7 @@
#include <AK/Memory.h> #include <AK/Memory.h>
#include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/CryptoKeyPrototype.h> #include <LibWeb/Bindings/CryptoKeyPrototype.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Crypto/CryptoKey.h> #include <LibWeb/Crypto/CryptoKey.h>

View file

@ -9,6 +9,7 @@
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/PrimitiveString.h> #include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Crypto/KeyAlgorithms.h> #include <LibWeb/Crypto/KeyAlgorithms.h>

View file

@ -11,6 +11,7 @@
#include <LibJS/Runtime/PromiseCapability.h> #include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/Realm.h> #include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/VM.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Export.h> #include <LibWeb/Export.h>
#include <LibWeb/WebIDL/Promise.h> #include <LibWeb/WebIDL/Promise.h>

View file

@ -5600,6 +5600,7 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface,
#include <LibJS/Runtime/FunctionObject.h> #include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/@prototype_class@.h> #include <LibWeb/Bindings/@prototype_class@.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>

View file

@ -9,6 +9,7 @@
#include <LibJS/Runtime/ArrayBuffer.h> #include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/Date.h> #include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibTest/JavaScriptTestRunner.h> #include <LibTest/JavaScriptTestRunner.h>
#include <LibUnicode/TimeZone.h> #include <LibUnicode/TimeZone.h>