mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 17:49:40 +00:00
LibJS: Allow GetById to cache getters
1.25x speed-up on this microbenchmark: let o = { get x() { return 1; } }; for (let i = 0; i < 10_000_000; ++i) o.x; I looked into this because I noticed getter invocation when profiling long-running WPT tests. We already had the mechanism for non-getter properties, and the change to support getters turned out to be trivial.
This commit is contained in:
parent
e4245dc39e
commit
3c5819a6d2
Notes:
github-actions[bot]
2024-10-17 20:07:21 +00:00
Author: https://github.com/awesomekling
Commit: 3c5819a6d2
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1843
2 changed files with 38 additions and 22 deletions
|
@ -15,6 +15,7 @@
|
||||||
#include <LibJS/Bytecode/Label.h>
|
#include <LibJS/Bytecode/Label.h>
|
||||||
#include <LibJS/Bytecode/Op.h>
|
#include <LibJS/Bytecode/Op.h>
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
|
#include <LibJS/Runtime/Accessor.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/BigInt.h>
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||||
|
@ -991,11 +992,18 @@ inline ThrowCompletionOr<Value> get_by_id(VM& vm, Optional<IdentifierTableIndex>
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}();
|
}();
|
||||||
if (can_use_cache)
|
if (can_use_cache) {
|
||||||
return cache.prototype->get_direct(cache.property_offset.value());
|
auto value = cache.prototype->get_direct(cache.property_offset.value());
|
||||||
|
if (value.is_accessor())
|
||||||
|
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
} else if (&shape == cache.shape) {
|
} else if (&shape == cache.shape) {
|
||||||
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
|
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
|
||||||
return base_obj->get_direct(cache.property_offset.value());
|
auto value = base_obj->get_direct(cache.property_offset.value());
|
||||||
|
if (value.is_accessor())
|
||||||
|
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheablePropertyMetadata cacheable_metadata;
|
CacheablePropertyMetadata cacheable_metadata;
|
||||||
|
@ -1103,7 +1111,9 @@ inline ThrowCompletionOr<Value> get_global(Interpreter& interpreter, IdentifierT
|
||||||
// OPTIMIZATION: For global var bindings, if the shape of the global object hasn't changed,
|
// OPTIMIZATION: For global var bindings, if the shape of the global object hasn't changed,
|
||||||
// we can use the cached property offset.
|
// we can use the cached property offset.
|
||||||
if (&shape == cache.shape) {
|
if (&shape == cache.shape) {
|
||||||
return binding_object.get_direct(cache.property_offset.value());
|
auto value = binding_object.get_direct(cache.property_offset.value());
|
||||||
|
if (value.is_accessor())
|
||||||
|
return TRY(call(vm, value.as_accessor().getter(), js_undefined()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPTIMIZATION: For global lexical bindings, if the global declarative environment hasn't changed,
|
// OPTIMIZATION: For global lexical bindings, if the global declarative environment hasn't changed,
|
||||||
|
|
|
@ -911,26 +911,30 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
|
||||||
return parent->internal_get(property_key, receiver, cacheable_metadata, PropertyLookupPhase::PrototypeChain);
|
return parent->internal_get(property_key, receiver, cacheable_metadata, PropertyLookupPhase::PrototypeChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto update_inline_cache = [&] {
|
||||||
|
// Non-standard: If the caller has requested cacheable metadata and the property is an own property, fill it in.
|
||||||
|
if (!cacheable_metadata || !descriptor->property_offset.has_value() || !shape().is_cacheable())
|
||||||
|
return;
|
||||||
|
if (phase == PropertyLookupPhase::OwnProperty) {
|
||||||
|
*cacheable_metadata = CacheablePropertyMetadata {
|
||||||
|
.type = CacheablePropertyMetadata::Type::OwnProperty,
|
||||||
|
.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,
|
||||||
|
.property_offset = descriptor->property_offset.value(),
|
||||||
|
.prototype = this,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 3. If IsDataDescriptor(desc) is true, return desc.[[Value]].
|
// 3. If IsDataDescriptor(desc) is true, return desc.[[Value]].
|
||||||
if (descriptor->is_data_descriptor()) {
|
if (descriptor->is_data_descriptor()) {
|
||||||
// Non-standard: If the caller has requested cacheable metadata and the property is an own property, fill it in.
|
update_inline_cache();
|
||||||
if (cacheable_metadata && descriptor->property_offset.has_value() && shape().is_cacheable()) {
|
|
||||||
if (phase == PropertyLookupPhase::OwnProperty) {
|
|
||||||
*cacheable_metadata = CacheablePropertyMetadata {
|
|
||||||
.type = CacheablePropertyMetadata::Type::OwnProperty,
|
|
||||||
.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,
|
|
||||||
.property_offset = descriptor->property_offset.value(),
|
|
||||||
.prototype = this,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *descriptor->value;
|
return *descriptor->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,6 +948,8 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
|
||||||
if (!getter)
|
if (!getter)
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
|
|
||||||
|
update_inline_cache();
|
||||||
|
|
||||||
// 7. Return ? Call(getter, Receiver).
|
// 7. Return ? Call(getter, Receiver).
|
||||||
return TRY(call(vm, *getter, receiver));
|
return TRY(call(vm, *getter, receiver));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue