diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index a675a4992f..2ae3f3e2c3 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -39,25 +39,6 @@ Value require_object_coercible(GlobalObject& global_object, Value value) return value; } -// 7.3.10 GetMethod ( V, P ), https://tc39.es/ecma262/#sec-getmethod -Function* get_method(GlobalObject& global_object, Value value, PropertyName const& property_name) -{ - auto& vm = global_object.vm(); - auto* object = value.to_object(global_object); - if (vm.exception()) - return nullptr; - auto property_value = object->get(property_name); - if (vm.exception()) - return nullptr; - if (property_value.is_empty() || property_value.is_nullish()) - return nullptr; - if (!property_value.is_function()) { - vm.throw_exception(global_object, ErrorType::NotAFunction, property_value.to_string_without_side_effects()); - return nullptr; - } - return &property_value.as_function(); -} - // 7.3.18 LengthOfArrayLike ( obj ), https://tc39.es/ecma262/#sec-lengthofarraylike size_t length_of_array_like(GlobalObject& global_object, Object const& object) { diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/AbstractOperations.h index 1e00d90cb7..0042bb47c7 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.h @@ -18,7 +18,6 @@ ObjectEnvironmentRecord* new_object_environment(Object&, bool is_with_environmen EnvironmentRecord& get_this_environment(VM&); Object* get_super_constructor(VM&); Value require_object_coercible(GlobalObject&, Value); -Function* get_method(GlobalObject& global_object, Value, PropertyName const&); size_t length_of_array_like(GlobalObject&, Object const&); MarkedValueList create_list_from_array_like(GlobalObject&, Value, AK::Function(Value)> = {}); Function* species_constructor(GlobalObject&, Object const&, Function& default_constructor); diff --git a/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp index c8770a9682..1cf6b76152 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp @@ -101,7 +101,7 @@ void iterator_close(Object& iterator) vm.unwind(unwind_until, unwind_until_label); }; - auto return_method = get_method(global_object, &iterator, vm.names.return_); + auto return_method = Value(&iterator).get_method(global_object, vm.names.return_); if (!return_method) return restore_completion(); // If return is undefined, return Completion(completion). diff --git a/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp index 69e2d4894f..696c2c8010 100644 --- a/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp @@ -62,7 +62,7 @@ Object* ProxyObject::prototype() vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return nullptr; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.getPrototypeOf); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.getPrototypeOf); if (vm.exception()) return nullptr; if (!trap) @@ -108,7 +108,7 @@ bool ProxyObject::set_prototype(Object* object) vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.setPrototypeOf); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.setPrototypeOf); if (vm.exception()) return false; if (!trap) @@ -135,7 +135,7 @@ bool ProxyObject::is_extensible() const vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.isExtensible); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.isExtensible); if (vm.exception()) return false; if (!trap) @@ -158,7 +158,7 @@ bool ProxyObject::prevent_extensions() vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.preventExtensions); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.preventExtensions); if (vm.exception()) return false; if (!trap) @@ -181,7 +181,7 @@ Optional ProxyObject::get_own_property_descriptor(const Prop vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return {}; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.getOwnPropertyDescriptor); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.getOwnPropertyDescriptor); if (vm.exception()) return {}; if (!trap) @@ -232,7 +232,7 @@ bool ProxyObject::define_property(const StringOrSymbol& property_name, const Obj vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.defineProperty); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.defineProperty); if (vm.exception()) return false; if (!trap) @@ -279,7 +279,7 @@ bool ProxyObject::has_property(const PropertyName& name) const vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.has); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.has); if (vm.exception()) return false; if (!trap) @@ -319,7 +319,7 @@ Value ProxyObject::get(const PropertyName& name, Value receiver, AllowSideEffect } if (receiver.is_empty()) receiver = Value(const_cast(this)); - auto trap = get_method(global_object(), Value(&m_handler), vm.names.get); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.get); if (vm.exception()) return {}; if (!trap) @@ -352,7 +352,7 @@ bool ProxyObject::put(const PropertyName& name, Value value, Value receiver) } if (receiver.is_empty()) receiver = Value(const_cast(this)); - auto trap = get_method(global_object(), Value(&m_handler), vm.names.set); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.set); if (vm.exception()) return false; if (!trap) @@ -382,7 +382,7 @@ bool ProxyObject::delete_property(PropertyName const& name, bool force_throw_exc vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.deleteProperty); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.deleteProperty); if (vm.exception()) return false; if (!trap) @@ -422,7 +422,7 @@ Value ProxyObject::call() vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return {}; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.apply); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.apply); if (vm.exception()) return {}; if (!trap) @@ -451,7 +451,7 @@ Value ProxyObject::construct(Function& new_target) vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return {}; } - auto trap = get_method(global_object(), Value(&m_handler), vm.names.construct); + auto trap = Value(&m_handler).get_method(global_object(), vm.names.construct); if (vm.exception()) return {}; if (!trap) diff --git a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp index 3471d5e39e..a8ec9db24a 100644 --- a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp @@ -262,7 +262,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match) return {}; bool global = global_value.to_boolean(); // FIXME: Implement and use RegExpExec, this does something different - https://tc39.es/ecma262/#sec-regexpexec - auto* exec = get_method(global_object, rx, vm.names.exec); + auto* exec = Value(rx).get_method(global_object, vm.names.exec); if (!exec) return js_undefined(); // FIXME end @@ -295,7 +295,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace) rx->regex().start_offset = 0; // FIXME: Implement and use RegExpExec - https://tc39.es/ecma262/#sec-regexpexec - auto* exec = get_method(global_object, rx, vm.names.exec); + auto* exec = Value(rx).get_method(global_object, vm.names.exec); if (!exec) return {}; diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp index 008dcef34b..a94608dbc1 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -702,7 +702,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match) return {}; auto regexp = vm.argument(0); if (!regexp.is_nullish()) { - if (auto* matcher = get_method(global_object, regexp, *vm.well_known_symbol_match())) + if (auto* matcher = regexp.get_method(global_object, *vm.well_known_symbol_match())) return vm.call(*matcher, regexp, this_object); } auto s = this_object.to_string(global_object); @@ -740,7 +740,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match_all) return {}; } } - if (auto* matcher = get_method(global_object, regexp, *vm.well_known_symbol_match_all())) + if (auto* matcher = regexp.get_method(global_object, *vm.well_known_symbol_match_all())) return vm.call(*matcher, regexp, this_object); if (vm.exception()) return {}; @@ -764,7 +764,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace) auto replace_value = vm.argument(1); if (!search_value.is_nullish()) { - if (auto* replacer = get_method(global_object, search_value, *vm.well_known_symbol_replace())) + if (auto* replacer = search_value.get_method(global_object, *vm.well_known_symbol_replace())) return vm.call(*replacer, search_value, this_object, replace_value); } @@ -812,7 +812,7 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::search) return {}; auto regexp = vm.argument(0); if (!regexp.is_nullish()) { - if (auto* searcher = get_method(global_object, regexp, *vm.well_known_symbol_search())) + if (auto* searcher = regexp.get_method(global_object, *vm.well_known_symbol_search())) return vm.call(*searcher, regexp, this_object); if (vm.exception()) return {}; diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index 35a1d18a0b..e2b0eac70d 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -413,7 +413,7 @@ Value Value::to_primitive(GlobalObject& global_object, PreferredType preferred_t }; if (is_object()) { auto& vm = global_object.vm(); - auto to_primitive_method = get_method(global_object, *this, *vm.well_known_symbol_to_primitive()); + auto to_primitive_method = get_method(global_object, *vm.well_known_symbol_to_primitive()); if (vm.exception()) return {}; if (to_primitive_method) { @@ -805,6 +805,32 @@ Value Value::get(GlobalObject& global_object, PropertyName const& property_name) return object->get(property_name, *this); } +// 7.3.10 GetMethod ( V, P ), https://tc39.es/ecma262/#sec-getmethod +Function* Value::get_method(GlobalObject& global_object, PropertyName const& property_name) const +{ + auto& vm = global_object.vm(); + + // 1. Assert: IsPropertyKey(P) is true. + + // 2. Let func be ? GetV(V, P). + auto function = get(global_object, property_name).value_or(js_undefined()); + if (vm.exception()) + return nullptr; + + // 3. If func is either undefined or null, return undefined. + if (function.is_nullish()) + return nullptr; + + // 4. If IsCallable(func) is false, throw a TypeError exception. + if (!function.is_function()) { + vm.throw_exception(global_object, ErrorType::NotAFunction, function.to_string_without_side_effects()); + return nullptr; + } + + // 5. Return func. + return &function.as_function(); +} + // 13.10 Relational Operators, https://tc39.es/ecma262/#sec-relational-operators Value greater_than(GlobalObject& global_object, Value lhs, Value rhs) { @@ -1204,7 +1230,7 @@ Value instance_of(GlobalObject& global_object, Value lhs, Value rhs) vm.throw_exception(global_object, ErrorType::NotAnObject, rhs.to_string_without_side_effects()); return {}; } - auto has_instance_method = get_method(global_object, Value(&rhs.as_object()), *vm.well_known_symbol_has_instance()); + auto has_instance_method = rhs.get_method(global_object, *vm.well_known_symbol_has_instance()); if (vm.exception()) return {}; if (has_instance_method) { diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index e7217af3ba..16d08f2a29 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -288,6 +288,7 @@ public: bool to_boolean() const; Value get(GlobalObject&, PropertyName const&) const; + Function* get_method(GlobalObject&, PropertyName const&) const; String to_string_without_side_effects() const;