mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
Compare commits
3 commits
3716db1c61
...
d065171791
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d065171791 | ||
![]() |
0fb9ba1e3a | ||
![]() |
26c1dea22a |
15 changed files with 300 additions and 161 deletions
|
@ -16,6 +16,7 @@
|
|||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
#include <LibJS/Bytecode/Label.h>
|
||||
#include <LibJS/Bytecode/Op.h>
|
||||
#include <LibJS/Bytecode/PropertyAccess.h>
|
||||
#include <LibJS/Export.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Accessor.h>
|
||||
|
@ -922,27 +923,6 @@ ALWAYS_INLINE Completion throw_null_or_undefined_property_access(VM& vm, Value b
|
|||
return vm.throw_completion<TypeError>(ErrorType::ToObjectNullOrUndefined);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE GC::Ptr<Object> base_object_for_get_impl(VM& vm, Value base_value)
|
||||
{
|
||||
if (base_value.is_object()) [[likely]]
|
||||
return base_value.as_object();
|
||||
|
||||
// OPTIMIZATION: For various primitives we can avoid actually creating a new object for them.
|
||||
auto& realm = *vm.current_realm();
|
||||
if (base_value.is_string())
|
||||
return realm.intrinsics().string_prototype();
|
||||
if (base_value.is_number())
|
||||
return realm.intrinsics().number_prototype();
|
||||
if (base_value.is_boolean())
|
||||
return realm.intrinsics().boolean_prototype();
|
||||
if (base_value.is_bigint())
|
||||
return realm.intrinsics().bigint_prototype();
|
||||
if (base_value.is_symbol())
|
||||
return realm.intrinsics().symbol_prototype();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> base_object_for_get(VM& vm, Value base_value, Optional<IdentifierTableIndex> base_identifier, IdentifierTableIndex property_identifier, Executable const& executable)
|
||||
{
|
||||
if (auto base_object = base_object_for_get_impl(vm, base_value))
|
||||
|
@ -961,92 +941,6 @@ ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> base_object_for_get(VM& vm, Val
|
|||
return throw_null_or_undefined_property_get(vm, base_value, base_identifier, property, executable);
|
||||
}
|
||||
|
||||
enum class GetByIdMode {
|
||||
Normal,
|
||||
Length,
|
||||
};
|
||||
|
||||
template<GetByIdMode mode = GetByIdMode::Normal>
|
||||
inline ThrowCompletionOr<Value> get_by_id(VM& vm, Optional<IdentifierTableIndex> base_identifier, IdentifierTableIndex property, Value base_value, Value this_value, PropertyLookupCache& cache, Executable const& executable)
|
||||
{
|
||||
if constexpr (mode == GetByIdMode::Length) {
|
||||
if (base_value.is_string()) {
|
||||
return Value(base_value.as_string().length_in_utf16_code_units());
|
||||
}
|
||||
}
|
||||
|
||||
auto base_obj = TRY(base_object_for_get(vm, base_value, base_identifier, property, executable));
|
||||
|
||||
if constexpr (mode == GetByIdMode::Length) {
|
||||
// OPTIMIZATION: Fast path for the magical "length" property on Array objects.
|
||||
if (base_obj->has_magical_length_property()) {
|
||||
return Value { base_obj->indexed_properties().array_like_size() };
|
||||
}
|
||||
}
|
||||
|
||||
auto& shape = base_obj->shape();
|
||||
|
||||
GC::Ptr<PrototypeChainValidity> prototype_chain_validity;
|
||||
if (shape.prototype())
|
||||
prototype_chain_validity = shape.prototype()->shape().prototype_chain_validity();
|
||||
|
||||
for (auto& cache_entry : cache.entries) {
|
||||
if (cache_entry.prototype) {
|
||||
// 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 (&shape != cache_entry.shape)
|
||||
return false;
|
||||
if (!cache_entry.prototype_chain_validity)
|
||||
return false;
|
||||
if (!cache_entry.prototype_chain_validity->is_valid())
|
||||
return false;
|
||||
return true;
|
||||
}();
|
||||
if (can_use_cache) {
|
||||
auto value = cache_entry.prototype->get_direct(cache_entry.property_offset.value());
|
||||
if (value.is_accessor())
|
||||
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||
return value;
|
||||
}
|
||||
} else if (&shape == cache_entry.shape) {
|
||||
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
|
||||
auto value = base_obj->get_direct(cache_entry.property_offset.value());
|
||||
if (value.is_accessor())
|
||||
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// that collected metadata is valid, e.g. if getter in prototype chain added
|
||||
// property with the same name into the object itself.
|
||||
if (&shape == &base_obj->shape()) {
|
||||
auto get_cache_slot = [&] -> PropertyLookupCache::Entry& {
|
||||
for (size_t i = cache.entries.size() - 1; i >= 1; --i) {
|
||||
cache.entries[i] = cache.entries[i - 1];
|
||||
}
|
||||
cache.entries[0] = {};
|
||||
return cache.entries[0];
|
||||
};
|
||||
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 == CacheableGetPropertyMetadata::Type::GetPropertyInPrototypeChain) {
|
||||
auto& entry = get_cache_slot();
|
||||
entry.shape = &base_obj->shape();
|
||||
entry.property_offset = cacheable_metadata.property_offset.value();
|
||||
entry.prototype = *cacheable_metadata.prototype;
|
||||
entry.prototype_chain_validity = *prototype_chain_validity;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline ThrowCompletionOr<Value> get_by_value(VM& vm, Optional<IdentifierTableIndex> base_identifier, Value base_value, Value property_key_value, Executable const& executable)
|
||||
{
|
||||
// OPTIMIZATION: Fast path for simple Int32 indexes in array-like objects.
|
||||
|
@ -2637,7 +2531,7 @@ ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter
|
|||
auto base_value = interpreter.get(base());
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
|
||||
|
||||
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), m_base_identifier, m_property, base_value, base_value, cache, interpreter.current_executable())));
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Normal>(interpreter.vm(), [&] { return interpreter.get_identifier(m_base_identifier); }, [&] { return interpreter.get_identifier(m_property); }, base_value, base_value, cache)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -2646,7 +2540,7 @@ ThrowCompletionOr<void> GetByIdWithThis::execute_impl(Bytecode::Interpreter& int
|
|||
auto base_value = interpreter.get(m_base);
|
||||
auto this_value = interpreter.get(m_this_value);
|
||||
auto& cache = interpreter.current_executable().property_lookup_caches[m_cache_index];
|
||||
interpreter.set(dst(), TRY(get_by_id(interpreter.vm(), {}, m_property, base_value, this_value, cache, interpreter.current_executable())));
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Normal>(interpreter.vm(), [] { return Optional<Utf16FlyString const&> {}; }, [&] { return interpreter.get_identifier(m_property); }, base_value, this_value, cache)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -2656,7 +2550,7 @@ ThrowCompletionOr<void> GetLength::execute_impl(Bytecode::Interpreter& interpret
|
|||
auto& executable = interpreter.current_executable();
|
||||
auto& cache = executable.property_lookup_caches[m_cache_index];
|
||||
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Length>(interpreter.vm(), m_base_identifier, *executable.length_identifier, base_value, base_value, cache, executable)));
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Length>(interpreter.vm(), [&] { return interpreter.get_identifier(m_base_identifier); }, [&] { return executable.get_identifier(*executable.length_identifier); }, base_value, base_value, cache)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -2666,7 +2560,7 @@ ThrowCompletionOr<void> GetLengthWithThis::execute_impl(Bytecode::Interpreter& i
|
|||
auto this_value = interpreter.get(m_this_value);
|
||||
auto& executable = interpreter.current_executable();
|
||||
auto& cache = executable.property_lookup_caches[m_cache_index];
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Length>(interpreter.vm(), {}, *executable.length_identifier, base_value, this_value, cache, executable)));
|
||||
interpreter.set(dst(), TRY(get_by_id<GetByIdMode::Length>(interpreter.vm(), [] { return Optional<Utf16FlyString const&> {}; }, [&] { return executable.get_identifier(*executable.length_identifier); }, base_value, this_value, cache)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
152
Libraries/LibJS/Bytecode/PropertyAccess.h
Normal file
152
Libraries/LibJS/Bytecode/PropertyAccess.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2025, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Accessor.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/Shape.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWasm/Opcode.h>
|
||||
|
||||
namespace JS::Bytecode {
|
||||
|
||||
enum class GetByIdMode {
|
||||
Normal,
|
||||
Length,
|
||||
};
|
||||
|
||||
ALWAYS_INLINE GC::Ptr<Object> base_object_for_get_impl(VM& vm, Value base_value)
|
||||
{
|
||||
if (base_value.is_object()) [[likely]]
|
||||
return base_value.as_object();
|
||||
|
||||
// OPTIMIZATION: For various primitives we can avoid actually creating a new object for them.
|
||||
auto& realm = *vm.current_realm();
|
||||
if (base_value.is_string())
|
||||
return realm.intrinsics().string_prototype();
|
||||
if (base_value.is_number())
|
||||
return realm.intrinsics().number_prototype();
|
||||
if (base_value.is_boolean())
|
||||
return realm.intrinsics().boolean_prototype();
|
||||
if (base_value.is_bigint())
|
||||
return realm.intrinsics().bigint_prototype();
|
||||
if (base_value.is_symbol())
|
||||
return realm.intrinsics().symbol_prototype();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename GetBaseIdentifier, typename GetPropertyName>
|
||||
ALWAYS_INLINE Completion throw_null_or_undefined_property_get(VM& vm, Value base_value, GetBaseIdentifier get_base_identifier, GetPropertyName get_property_name)
|
||||
{
|
||||
VERIFY(base_value.is_nullish());
|
||||
|
||||
auto base_identifier = get_base_identifier();
|
||||
if (base_identifier.has_value())
|
||||
return vm.throw_completion<TypeError>(ErrorType::ToObjectNullOrUndefinedWithPropertyAndName, get_property_name(), base_value, base_identifier);
|
||||
return vm.throw_completion<TypeError>(ErrorType::ToObjectNullOrUndefinedWithProperty, get_property_name(), base_value);
|
||||
}
|
||||
|
||||
template<typename GetBaseIdentifier, typename GetPropertyName>
|
||||
ALWAYS_INLINE ThrowCompletionOr<GC::Ref<Object>> base_object_for_get(VM& vm, Value base_value, GetBaseIdentifier get_base_identifier, GetPropertyName get_property_name)
|
||||
{
|
||||
if (auto base_object = base_object_for_get_impl(vm, base_value))
|
||||
return GC::Ref { *base_object };
|
||||
|
||||
// NOTE: At this point this is guaranteed to throw (null or undefined).
|
||||
return throw_null_or_undefined_property_get(vm, base_value, get_base_identifier, get_property_name);
|
||||
}
|
||||
|
||||
template<GetByIdMode mode, typename GetBaseIdentifier, typename GetPropertyName>
|
||||
ALWAYS_INLINE ThrowCompletionOr<Value> get_by_id(VM& vm, GetBaseIdentifier get_base_identifier, GetPropertyName get_property_name, Value base_value, Value this_value, PropertyLookupCache& cache)
|
||||
{
|
||||
if constexpr (mode == GetByIdMode::Length) {
|
||||
if (base_value.is_string()) {
|
||||
return Value(base_value.as_string().length_in_utf16_code_units());
|
||||
}
|
||||
}
|
||||
|
||||
auto base_obj = TRY(base_object_for_get(vm, base_value, get_base_identifier, get_property_name));
|
||||
|
||||
if constexpr (mode == GetByIdMode::Length) {
|
||||
// OPTIMIZATION: Fast path for the magical "length" property on Array objects.
|
||||
if (base_obj->has_magical_length_property()) {
|
||||
return Value { base_obj->indexed_properties().array_like_size() };
|
||||
}
|
||||
}
|
||||
|
||||
auto& shape = base_obj->shape();
|
||||
|
||||
GC::Ptr<PrototypeChainValidity> prototype_chain_validity;
|
||||
if (shape.prototype())
|
||||
prototype_chain_validity = shape.prototype()->shape().prototype_chain_validity();
|
||||
|
||||
for (auto& cache_entry : cache.entries) {
|
||||
if (cache_entry.prototype) {
|
||||
// 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 (&shape != cache_entry.shape) [[unlikely]]
|
||||
return false;
|
||||
if (!cache_entry.prototype_chain_validity) [[unlikely]]
|
||||
return false;
|
||||
if (!cache_entry.prototype_chain_validity->is_valid()) [[unlikely]]
|
||||
return false;
|
||||
return true;
|
||||
}();
|
||||
if (can_use_cache) [[likely]] {
|
||||
auto value = cache_entry.prototype->get_direct(cache_entry.property_offset.value());
|
||||
if (value.is_accessor())
|
||||
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||
return value;
|
||||
}
|
||||
} else if (&shape == cache_entry.shape) {
|
||||
// OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset.
|
||||
auto value = base_obj->get_direct(cache_entry.property_offset.value());
|
||||
if (value.is_accessor()) {
|
||||
return TRY(call(vm, value.as_accessor().getter(), this_value));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
CacheableGetPropertyMetadata cacheable_metadata;
|
||||
auto value = TRY(base_obj->internal_get(get_property_name(), this_value, &cacheable_metadata));
|
||||
|
||||
// If internal_get() caused object's shape change, we can no longer be sure
|
||||
// that collected metadata is valid, e.g. if getter in prototype chain added
|
||||
// property with the same name into the object itself.
|
||||
if (&shape == &base_obj->shape()) {
|
||||
auto get_cache_slot = [&] -> PropertyLookupCache::Entry& {
|
||||
for (size_t i = cache.entries.size() - 1; i >= 1; --i) {
|
||||
cache.entries[i] = cache.entries[i - 1];
|
||||
}
|
||||
cache.entries[0] = {};
|
||||
return cache.entries[0];
|
||||
};
|
||||
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 == CacheableGetPropertyMetadata::Type::GetPropertyInPrototypeChain) {
|
||||
auto& entry = get_cache_slot();
|
||||
entry.shape = &base_obj->shape();
|
||||
entry.property_offset = cacheable_metadata.property_offset.value();
|
||||
entry.prototype = *cacheable_metadata.prototype;
|
||||
entry.prototype_chain_validity = *prototype_chain_validity;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
|
@ -327,6 +327,7 @@ class Generator;
|
|||
class Instruction;
|
||||
class Interpreter;
|
||||
class Operand;
|
||||
struct PropertyLookupCache;
|
||||
class RegexTable;
|
||||
class Register;
|
||||
|
||||
|
|
|
@ -127,7 +127,8 @@ ThrowCompletionOr<size_t> length_of_array_like(VM& vm, Object const& object)
|
|||
return object.indexed_properties().array_like_size();
|
||||
|
||||
// 1. Return ℝ(? ToLength(? Get(obj, "length"))).
|
||||
return TRY(object.get(vm.names.length)).to_length(vm);
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
return TRY(object.get(vm.names.length, cache)).to_length(vm);
|
||||
}
|
||||
|
||||
// 7.3.20 CreateListFromArrayLike ( obj [ , elementTypes ] ), https://tc39.es/ecma262/#sec-createlistfromarraylike
|
||||
|
@ -173,7 +174,8 @@ ThrowCompletionOr<GC::RootVector<Value>> create_list_from_array_like(VM& vm, Val
|
|||
ThrowCompletionOr<FunctionObject*> species_constructor(VM& vm, Object const& object, FunctionObject& default_constructor)
|
||||
{
|
||||
// 1. Let C be ? Get(O, "constructor").
|
||||
auto constructor = TRY(object.get(vm.names.constructor));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto constructor = TRY(object.get(vm.names.constructor, cache));
|
||||
|
||||
// 2. If C is undefined, return defaultConstructor.
|
||||
if (constructor.is_undefined())
|
||||
|
@ -184,7 +186,8 @@ ThrowCompletionOr<FunctionObject*> species_constructor(VM& vm, Object const& obj
|
|||
return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, constructor.to_string_without_side_effects());
|
||||
|
||||
// 4. Let S be ? Get(C, @@species).
|
||||
auto species = TRY(constructor.as_object().get(vm.well_known_symbol_species()));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto species = TRY(constructor.as_object().get(vm.well_known_symbol_species(), cache2));
|
||||
|
||||
// 5. If S is either undefined or null, return defaultConstructor.
|
||||
if (species.is_nullish())
|
||||
|
@ -407,7 +410,8 @@ ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject
|
|||
// 1. Assert: intrinsicDefaultProto is this specification's name of an intrinsic object. The corresponding object must be an intrinsic that is intended to be used as the [[Prototype]] value of an object.
|
||||
|
||||
// 2. Let proto be ? Get(constructor, "prototype").
|
||||
auto prototype = TRY(constructor.get(vm.names.prototype));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto prototype = TRY(constructor.get(vm.names.prototype, cache));
|
||||
|
||||
// 3. If Type(proto) is not Object, then
|
||||
if (!prototype.is_object()) {
|
||||
|
|
|
@ -119,7 +119,8 @@ static ThrowCompletionOr<Object*> array_species_create(VM& vm, Object& original_
|
|||
if (!is_array)
|
||||
return TRY(Array::create(realm, length)).ptr();
|
||||
|
||||
auto constructor = TRY(original_array.get(vm.names.constructor));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto constructor = TRY(original_array.get(vm.names.constructor, cache));
|
||||
if (constructor.is_constructor()) {
|
||||
auto& constructor_function = constructor.as_function();
|
||||
auto* this_realm = vm.current_realm();
|
||||
|
@ -131,7 +132,8 @@ static ThrowCompletionOr<Object*> array_species_create(VM& vm, Object& original_
|
|||
}
|
||||
|
||||
if (constructor.is_object()) {
|
||||
constructor = TRY(constructor.as_object().get(vm.well_known_symbol_species()));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
constructor = TRY(constructor.as_object().get(vm.well_known_symbol_species(), cache2));
|
||||
if (constructor.is_null())
|
||||
constructor = js_undefined();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ ThrowCompletionOr<GC::Ref<AsyncGenerator>> AsyncGenerator::create(Realm& realm,
|
|||
{
|
||||
auto& vm = realm.vm();
|
||||
// This is "g1.prototype" in figure-2 (https://tc39.es/ecma262/img/figure-2.png)
|
||||
auto generating_function_prototype = TRY(generating_function->get(vm.names.prototype));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto generating_function_prototype = TRY(generating_function->get(vm.names.prototype, cache));
|
||||
auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm));
|
||||
auto object = realm.create<AsyncGenerator>(realm, generating_function_prototype_object, move(execution_context));
|
||||
object->m_generating_function = generating_function;
|
||||
|
|
|
@ -29,7 +29,8 @@ ThrowCompletionOr<GC::Ref<GeneratorObject>> GeneratorObject::create(Realm& realm
|
|||
// changed thus we hardcode the prototype.
|
||||
generating_function_prototype = realm.intrinsics().generator_prototype();
|
||||
} else {
|
||||
generating_function_prototype = TRY(generating_function->get(vm.names.prototype));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
generating_function_prototype = TRY(generating_function->get(vm.names.prototype, cache));
|
||||
}
|
||||
auto generating_function_prototype_object = TRY(generating_function_prototype.to_object(vm));
|
||||
auto object = realm.create<GeneratorObject>(realm, generating_function_prototype_object, move(execution_context));
|
||||
|
|
|
@ -39,7 +39,8 @@ Iterator::Iterator(Object& prototype)
|
|||
ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_direct(VM& vm, Object& object)
|
||||
{
|
||||
// 1. Let nextMethod be ? Get(obj, "next").
|
||||
auto next_method = TRY(object.get(vm.names.next));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto next_method = TRY(object.get(vm.names.next, cache));
|
||||
|
||||
// 2. Let iteratorRecord be Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
|
||||
// 3. Return iteratorRecord.
|
||||
|
@ -57,7 +58,8 @@ ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_from_method(VM& vm, Valu
|
|||
return vm.throw_completion<TypeError>(ErrorType::NotIterable, object.to_string_without_side_effects());
|
||||
|
||||
// 3. Let nextMethod be ? Get(iterator, "next").
|
||||
auto next_method = TRY(iterator.get(vm, vm.names.next));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto next_method = TRY(iterator.get(vm, vm.names.next, cache));
|
||||
|
||||
// 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
|
||||
auto iterator_record = vm.heap().allocate<IteratorRecord>(iterator.as_object(), next_method, false);
|
||||
|
@ -79,7 +81,8 @@ ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator(VM& vm, Value object, It
|
|||
// b. If method is undefined, then
|
||||
if (!method) {
|
||||
// i. Let syncMethod be ? GetMethod(obj, @@iterator).
|
||||
auto sync_method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto sync_method = TRY(object.get_method(vm, vm.well_known_symbol_iterator(), cache));
|
||||
|
||||
// ii. If syncMethod is undefined, throw a TypeError exception.
|
||||
if (!sync_method)
|
||||
|
@ -95,7 +98,8 @@ ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator(VM& vm, Value object, It
|
|||
// 2. Else,
|
||||
else {
|
||||
// a. Let method be ? GetMethod(obj, @@iterator).
|
||||
method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
method = TRY(object.get_method(vm, vm.well_known_symbol_iterator(), cache));
|
||||
}
|
||||
|
||||
// 3. If method is undefined, throw a TypeError exception.
|
||||
|
@ -124,7 +128,8 @@ ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_flattenable(VM& vm, Valu
|
|||
}
|
||||
|
||||
// 2. Let method be ? GetMethod(obj, %Symbol.iterator%).
|
||||
auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator(), cache));
|
||||
|
||||
Value iterator;
|
||||
|
||||
|
@ -192,14 +197,16 @@ ThrowCompletionOr<GC::Ref<Object>> iterator_next(VM& vm, IteratorRecord& iterato
|
|||
ThrowCompletionOr<bool> iterator_complete(VM& vm, Object& iterator_result)
|
||||
{
|
||||
// 1. Return ToBoolean(? Get(iterResult, "done")).
|
||||
return TRY(iterator_result.get(vm.names.done)).to_boolean();
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
return TRY(iterator_result.get(vm.names.done, cache)).to_boolean();
|
||||
}
|
||||
|
||||
// 7.4.8 IteratorValue ( iteratorResult ), https://tc39.es/ecma262/#sec-iteratorvalue
|
||||
ThrowCompletionOr<Value> iterator_value(VM& vm, Object& iterator_result)
|
||||
{
|
||||
// 1. Return ? Get(iterResult, "value").
|
||||
return TRY(iterator_result.get(vm.names.value));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
return TRY(iterator_result.get(vm.names.value, cache));
|
||||
}
|
||||
|
||||
// 7.4.9 IteratorStep ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstep
|
||||
|
@ -220,7 +227,8 @@ ThrowCompletionOr<IterationResultOrDone> iterator_step(VM& vm, IteratorRecord& i
|
|||
auto result = TRY(iterator_next(vm, iterator_record));
|
||||
|
||||
// 2. Let done be Completion(IteratorComplete(result)).
|
||||
auto done = result->get(vm.names.done);
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto done = result->get(vm.names.done, cache);
|
||||
|
||||
// 3. If done is a throw completion, then
|
||||
if (done.is_throw_completion()) {
|
||||
|
@ -244,7 +252,8 @@ ThrowCompletionOr<IterationResultOrDone> iterator_step(VM& vm, IteratorRecord& i
|
|||
}
|
||||
|
||||
// 6. Return result.
|
||||
return ThrowCompletionOr<IterationResultOrDone> { IterationResult { done_value, result->get(vm.names.value) } };
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
return ThrowCompletionOr<IterationResultOrDone> { IterationResult { done_value, result->get(vm.names.value, cache2) } };
|
||||
}
|
||||
|
||||
// 7.4.10 IteratorStepValue ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstepvalue
|
||||
|
|
|
@ -125,6 +125,13 @@ ThrowCompletionOr<Value> Object::get(PropertyKey const& property_key) const
|
|||
return TRY(internal_get(property_key, this));
|
||||
}
|
||||
|
||||
// 7.3.2 Get ( O, P ), https://tc39.es/ecma262/#sec-get-o-p
|
||||
ThrowCompletionOr<Value> Object::get(PropertyKey const& property_key, Bytecode::PropertyLookupCache& cache) const
|
||||
{
|
||||
// 1. Return ? O.[[Get]](P, O).
|
||||
return TRY(Value(this).get(vm(), property_key, cache));
|
||||
}
|
||||
|
||||
// NOTE: 7.3.3 GetV ( V, P ) is implemented as Value::get().
|
||||
|
||||
// 7.3.4 Set ( O, P, V, Throw ), https://tc39.es/ecma262/#sec-set-o-p-v-throw
|
||||
|
@ -1540,7 +1547,15 @@ ThrowCompletionOr<Value> Object::ordinary_to_primitive(Value::PreferredType pref
|
|||
// 3. For each element name of methodNames, do
|
||||
for (auto& method_name : method_names) {
|
||||
// a. Let method be ? Get(O, name).
|
||||
auto method = TRY(get(method_name));
|
||||
Value method;
|
||||
if (method_name == vm.names.toString) {
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
method = TRY(get(method_name, cache));
|
||||
} else {
|
||||
ASSERT(method_name == vm.names.valueOf);
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
method = TRY(get(method_name, cache));
|
||||
}
|
||||
|
||||
// b. If IsCallable(method) is true, then
|
||||
if (method.is_function()) {
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
// 7.3 Operations on Objects, https://tc39.es/ecma262/#sec-operations-on-objects
|
||||
|
||||
ThrowCompletionOr<Value> get(PropertyKey const&) const;
|
||||
ThrowCompletionOr<Value> get(PropertyKey const&, Bytecode::PropertyLookupCache&) const;
|
||||
ThrowCompletionOr<void> set(PropertyKey const&, Value, ShouldThrowExceptions);
|
||||
ThrowCompletionOr<bool> create_data_property(PropertyKey const&, Value, Optional<u32>* new_property_offset = nullptr);
|
||||
void create_method_property(PropertyKey const&, Value);
|
||||
|
|
|
@ -182,7 +182,8 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
|
|||
builtin_tag = "Object"sv;
|
||||
|
||||
// 15. Let tag be ? Get(O, @@toStringTag).
|
||||
auto to_string_tag = TRY(object->get(vm.well_known_symbol_to_string_tag()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto to_string_tag = TRY(object->get(vm.well_known_symbol_to_string_tag(), cache));
|
||||
|
||||
// Optimization: Instead of creating another PrimitiveString from builtin_tag, we separate tag and to_string_tag and add an additional branch to step 16.
|
||||
StringView tag;
|
||||
|
|
|
@ -61,7 +61,8 @@ void RegExpPrototype::initialize(Realm& realm)
|
|||
static ThrowCompletionOr<void> increment_last_index(VM& vm, Object& regexp_object, Utf16View const& string, bool unicode)
|
||||
{
|
||||
// Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))).
|
||||
auto last_index_value = TRY(regexp_object.get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto last_index_value = TRY(regexp_object.get(vm.names.lastIndex, cache));
|
||||
auto last_index = TRY(last_index_value.to_length(vm));
|
||||
|
||||
// Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
|
||||
|
@ -176,7 +177,8 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
|
|||
|
||||
// 1. Let length be the length of S.
|
||||
// 2. Let lastIndex be ℝ(? ToLength(? Get(R, "lastIndex"))).
|
||||
auto last_index_value = TRY(regexp_object.get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto last_index_value = TRY(regexp_object.get(vm.names.lastIndex, cache));
|
||||
auto last_index = TRY(last_index_value.to_length(vm));
|
||||
|
||||
auto const& regex = regexp_object.regex();
|
||||
|
@ -393,7 +395,8 @@ static ThrowCompletionOr<Value> regexp_builtin_exec(VM& vm, RegExpObject& regexp
|
|||
ThrowCompletionOr<Value> regexp_exec(VM& vm, Object& regexp_object, GC::Ref<PrimitiveString> string)
|
||||
{
|
||||
// 1. Let exec be ? Get(R, "exec").
|
||||
auto exec = TRY(regexp_object.get(vm.names.exec));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto exec = TRY(regexp_object.get(vm.names.exec, cache));
|
||||
|
||||
// 2. If IsCallable(exec) is true, then
|
||||
if (exec.is_function()) {
|
||||
|
@ -508,10 +511,13 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::flags)
|
|||
// 17. If unicodeSets is true, append the code unit 0x0076 (LATIN SMALL LETTER V) as the last code unit of result.
|
||||
// 18. Let sticky be ToBoolean(? Get(R, "sticky")).
|
||||
// 19. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) as the last code unit of result.
|
||||
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
|
||||
auto flag_##flag_name = TRY(regexp_object->get(vm.names.flagName)); \
|
||||
if (flag_##flag_name.to_boolean()) \
|
||||
builder.append(#flag_char##sv);
|
||||
#define __JS_ENUMERATE(FlagName, flagName, flag_name, flag_char) \
|
||||
{ \
|
||||
static Bytecode::PropertyLookupCache cache; \
|
||||
auto flag_##flag_name = TRY(regexp_object->get(vm.names.flagName, cache)); \
|
||||
if (flag_##flag_name.to_boolean()) \
|
||||
builder.append(#flag_char##sv); \
|
||||
}
|
||||
JS_ENUMERATE_REGEXP_FLAGS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
|
@ -532,7 +538,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
|
|||
auto string = TRY(vm.argument(0).to_primitive_string(vm));
|
||||
|
||||
// 4. Let flags be ? ToString(? Get(rx, "flags")).
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// 5. If flags does not contain "g", then
|
||||
|
@ -608,7 +615,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match_all)
|
|||
auto* constructor = TRY(species_constructor(vm, regexp_object, realm.intrinsics().regexp_constructor()));
|
||||
|
||||
// 5. Let flags be ? ToString(? Get(R, "flags")).
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// Steps 9-12 are performed early so that flags can be moved.
|
||||
|
@ -625,7 +633,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match_all)
|
|||
auto matcher = TRY(construct(vm, *constructor, regexp_object, PrimitiveString::create(vm, move(flags))));
|
||||
|
||||
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
|
||||
auto last_index_value = TRY(regexp_object->get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto last_index_value = TRY(regexp_object->get(vm.names.lastIndex, cache2));
|
||||
auto last_index = TRY(last_index_value.to_length(vm));
|
||||
|
||||
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
|
||||
|
@ -659,7 +668,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
|||
}
|
||||
|
||||
// 7. Let flags be ? ToString(? Get(rx, "flags")).
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// 8. If flags contains "g", let global be true. Otherwise, let global be false.
|
||||
|
@ -731,7 +741,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
|||
auto matched_length = matched->length_in_utf16_code_units();
|
||||
|
||||
// e. Let position be ? ToIntegerOrInfinity(? Get(result, "index")).
|
||||
auto position_value = TRY(result->get(vm.names.index));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto position_value = TRY(result->get(vm.names.index, cache2));
|
||||
double position = TRY(position_value.to_integer_or_infinity(vm));
|
||||
|
||||
// f. Set position to the result of clamping position between 0 and lengthS.
|
||||
|
@ -760,7 +771,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
|
|||
}
|
||||
|
||||
// j. Let namedCaptures be ? Get(result, "groups").
|
||||
auto named_captures = TRY(result->get(vm.names.groups));
|
||||
static Bytecode::PropertyLookupCache cache3;
|
||||
auto named_captures = TRY(result->get(vm.names.groups, cache3));
|
||||
|
||||
String replacement;
|
||||
|
||||
|
@ -833,7 +845,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_search)
|
|||
auto string = TRY(vm.argument(0).to_primitive_string(vm));
|
||||
|
||||
// 4. Let previousLastIndex be ? Get(rx, "lastIndex").
|
||||
auto previous_last_index = TRY(regexp_object->get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto previous_last_index = TRY(regexp_object->get(vm.names.lastIndex, cache));
|
||||
|
||||
// 5. If SameValue(previousLastIndex, +0đť”˝) is false, then
|
||||
if (!same_value(previous_last_index, Value(0))) {
|
||||
|
@ -845,7 +858,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_search)
|
|||
auto result = TRY(regexp_exec(vm, regexp_object, string));
|
||||
|
||||
// 7. Let currentLastIndex be ? Get(rx, "lastIndex").
|
||||
auto current_last_index = TRY(regexp_object->get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto current_last_index = TRY(regexp_object->get(vm.names.lastIndex, cache2));
|
||||
|
||||
// 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
|
||||
if (!same_value(current_last_index, previous_last_index)) {
|
||||
|
@ -858,7 +872,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_search)
|
|||
return Value(-1);
|
||||
|
||||
// 10. Return ? Get(result, "index").
|
||||
return TRY(result.get(vm, vm.names.index));
|
||||
static Bytecode::PropertyLookupCache cache3;
|
||||
return TRY(result.get(vm, vm.names.index, cache3));
|
||||
}
|
||||
|
||||
// 22.2.6.13 get RegExp.prototype.source, https://tc39.es/ecma262/#sec-get-regexp.prototype.source
|
||||
|
@ -903,7 +918,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
|||
auto* constructor = TRY(species_constructor(vm, regexp_object, realm.intrinsics().regexp_constructor()));
|
||||
|
||||
// 5. Let flags be ? ToString(? Get(rx, "flags")).
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto flags_value = TRY(regexp_object->get(vm.names.flags, cache));
|
||||
auto flags = TRY(flags_value.to_string(vm));
|
||||
|
||||
// 6. If flags contains "u" or flags contains "v", let unicodeMatching be true.
|
||||
|
@ -973,7 +989,8 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_split)
|
|||
// d. Else,
|
||||
|
||||
// i. Let e be ℝ(? ToLength(? Get(splitter, "lastIndex"))).
|
||||
auto last_index_value = TRY(splitter->get(vm.names.lastIndex));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto last_index_value = TRY(splitter->get(vm.names.lastIndex, cache2));
|
||||
auto last_index = TRY(last_index_value.to_length(vm));
|
||||
|
||||
// ii. Set e to min(e, size).
|
||||
|
@ -1068,11 +1085,13 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
|
|||
auto regexp_object = TRY(this_object(vm));
|
||||
|
||||
// 3. Let pattern be ? ToString(? Get(R, "source")).
|
||||
auto source_attr = TRY(regexp_object->get(vm.names.source));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto source_attr = TRY(regexp_object->get(vm.names.source, cache));
|
||||
auto pattern = TRY(source_attr.to_string(vm));
|
||||
|
||||
// 4. Let flags be ? ToString(? Get(R, "flags")).
|
||||
auto flags_attr = TRY(regexp_object->get(vm.names.flags));
|
||||
static Bytecode::PropertyLookupCache cache2;
|
||||
auto flags_attr = TRY(regexp_object->get(vm.names.flags, cache2));
|
||||
auto flags = TRY(flags_attr.to_string(vm));
|
||||
|
||||
// 5. Let result be the string-concatenation of "/", pattern, "/", and flags.
|
||||
|
|
|
@ -587,7 +587,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match)
|
|||
auto regexp = vm.argument(0);
|
||||
if (regexp.is_object()) {
|
||||
// a. Let matcher be ? GetMethod(regexp, @@match).
|
||||
auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match(), cache));
|
||||
|
||||
// b. If matcher is not undefined, then
|
||||
if (matcher) {
|
||||
|
@ -634,7 +635,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match_all)
|
|||
}
|
||||
|
||||
// c. Let matcher be ? GetMethod(regexp, @@matchAll).
|
||||
auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match_all()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto matcher = TRY(regexp.get_method(vm, vm.well_known_symbol_match_all(), cache));
|
||||
|
||||
// d. If matcher is not undefined, then
|
||||
if (matcher) {
|
||||
|
@ -804,7 +806,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
|
|||
// 2. If searchValue is an Object, then
|
||||
if (search_value.is_object()) {
|
||||
// a. Let replacer be ? GetMethod(searchValue, @@replace).
|
||||
auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace(), cache));
|
||||
|
||||
// b. If replacer is not undefined, then
|
||||
if (replacer) {
|
||||
|
@ -899,7 +902,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
|
|||
}
|
||||
|
||||
// c. Let replacer be ? GetMethod(searchValue, @@replace).
|
||||
auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto replacer = TRY(search_value.get_method(vm, vm.well_known_symbol_replace(), cache));
|
||||
|
||||
// d. If replacer is not undefined, then
|
||||
if (replacer) {
|
||||
|
@ -999,7 +1003,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::search)
|
|||
// 2. If regexp is an Object, then
|
||||
if (regexp.is_object()) {
|
||||
// a. Let searcher be ? GetMethod(regexp, @@search).
|
||||
auto searcher = TRY(regexp.get_method(vm, vm.well_known_symbol_search()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto searcher = TRY(regexp.get_method(vm, vm.well_known_symbol_search(), cache));
|
||||
|
||||
// b. If searcher is not undefined, then
|
||||
if (searcher) {
|
||||
|
@ -1080,7 +1085,8 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
|
|||
// 2. If separator is an Object, then
|
||||
if (separator_argument.is_object()) {
|
||||
// a. Let splitter be ? GetMethod(separator, @@split).
|
||||
auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto splitter = TRY(separator_argument.get_method(vm, vm.well_known_symbol_split(), cache));
|
||||
// b. If splitter is not undefined, then
|
||||
if (splitter) {
|
||||
// i. Return ? Call(splitter, separator, « O, limit »).
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2020-2025, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||
*
|
||||
|
@ -15,6 +15,7 @@
|
|||
#include <AK/Utf16String.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Bytecode/PropertyAccess.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Accessor.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
|
@ -304,7 +305,8 @@ ThrowCompletionOr<bool> Value::is_regexp(VM& vm) const
|
|||
return false;
|
||||
|
||||
// 2. Let matcher be ? Get(argument, @@match).
|
||||
auto matcher = TRY(as_object().get(vm.well_known_symbol_match()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto matcher = TRY(as_object().get(vm.well_known_symbol_match(), cache));
|
||||
|
||||
// 3. If matcher is not undefined, return ToBoolean(matcher).
|
||||
if (!matcher.is_undefined())
|
||||
|
@ -568,7 +570,8 @@ ThrowCompletionOr<Value> Value::to_primitive_slow_case(VM& vm, PreferredType pre
|
|||
// 1. If input is an Object, then
|
||||
if (is_object()) {
|
||||
// a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
|
||||
auto exotic_to_primitive = TRY(get_method(vm, vm.well_known_symbol_to_primitive()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto exotic_to_primitive = TRY(get_method(vm, vm.well_known_symbol_to_primitive(), cache));
|
||||
|
||||
// b. If exoticToPrim is not undefined, then
|
||||
if (exotic_to_primitive) {
|
||||
|
@ -1306,6 +1309,13 @@ ThrowCompletionOr<Value> Value::get(VM& vm, PropertyKey const& property_key) con
|
|||
return TRY(object->internal_get(property_key, *this));
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Value> Value::get(VM& vm, PropertyKey const& property, Bytecode::PropertyLookupCache& cache) const
|
||||
{
|
||||
if (is_nullish())
|
||||
return vm.throw_completion<TypeError>(ErrorType::ToObjectNullOrUndefined);
|
||||
return Bytecode::get_by_id<Bytecode::GetByIdMode::Normal>(vm, [&]() { return Optional<Utf16FlyString const&> {}; }, [&]() { return property; }, *this, *this, cache);
|
||||
}
|
||||
|
||||
// 7.3.11 GetMethod ( V, P ), https://tc39.es/ecma262/#sec-getmethod
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> Value::get_method(VM& vm, PropertyKey const& property_key) const
|
||||
{
|
||||
|
@ -1324,6 +1334,24 @@ ThrowCompletionOr<GC::Ptr<FunctionObject>> Value::get_method(VM& vm, PropertyKey
|
|||
return function.as_function();
|
||||
}
|
||||
|
||||
// 7.3.11 GetMethod ( V, P ), https://tc39.es/ecma262/#sec-getmethod
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> Value::get_method(VM& vm, PropertyKey const& property_key, Bytecode::PropertyLookupCache& cache) const
|
||||
{
|
||||
// 1. Let func be ? GetV(V, P).
|
||||
auto function = TRY(get(vm, property_key, cache));
|
||||
|
||||
// 2. If func is either undefined or null, return undefined.
|
||||
if (function.is_nullish())
|
||||
return nullptr;
|
||||
|
||||
// 3. If IsCallable(func) is false, throw a TypeError exception.
|
||||
if (!function.is_function())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, function.to_string_without_side_effects());
|
||||
|
||||
// 4. Return func.
|
||||
return function.as_function();
|
||||
}
|
||||
|
||||
// 13.10 Relational Operators, https://tc39.es/ecma262/#sec-relational-operators
|
||||
// RelationalExpression : RelationalExpression > ShiftExpression
|
||||
ThrowCompletionOr<bool> greater_than(VM& vm, Value lhs, Value rhs)
|
||||
|
@ -2168,7 +2196,8 @@ ThrowCompletionOr<Value> instance_of(VM& vm, Value value, Value target)
|
|||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, target.to_string_without_side_effects());
|
||||
|
||||
// 2. Let instOfHandler be ? GetMethod(target, @@hasInstance).
|
||||
auto instance_of_handler = TRY(target.get_method(vm, vm.well_known_symbol_has_instance()));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto instance_of_handler = TRY(target.get_method(vm, vm.well_known_symbol_has_instance(), cache));
|
||||
|
||||
// 3. If instOfHandler is not undefined, then
|
||||
if (instance_of_handler) {
|
||||
|
@ -2215,7 +2244,8 @@ ThrowCompletionOr<Value> ordinary_has_instance(VM& vm, Value lhs, Value rhs)
|
|||
auto* lhs_object = &lhs.as_object();
|
||||
|
||||
// 4. Let P be ? Get(C, "prototype").
|
||||
auto rhs_prototype = TRY(rhs_function.get(vm.names.prototype));
|
||||
static Bytecode::PropertyLookupCache cache;
|
||||
auto rhs_prototype = TRY(rhs.get(vm, vm.names.prototype, cache));
|
||||
|
||||
// 5. If P is not an Object, throw a TypeError exception.
|
||||
if (!rhs_prototype.is_object())
|
||||
|
|
|
@ -382,7 +382,10 @@ public:
|
|||
bool to_boolean() const;
|
||||
|
||||
ThrowCompletionOr<Value> get(VM&, PropertyKey const&) const;
|
||||
ThrowCompletionOr<Value> get(VM&, PropertyKey const&, Bytecode::PropertyLookupCache&) const;
|
||||
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_method(VM&, PropertyKey const&) const;
|
||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_method(VM&, PropertyKey const&, Bytecode::PropertyLookupCache&) const;
|
||||
|
||||
[[nodiscard]] String to_string_without_side_effects() const;
|
||||
[[nodiscard]] Utf16String to_utf16_string_without_side_effects() const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue