diff options
author | Linus Groh <mail@linusgroh.de> | 2022-08-13 23:55:41 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-08-14 00:44:27 +0100 |
commit | 849495915b0084774a0b3d5a0e05f01434f79aa5 (patch) | |
tree | 79d6bbdc9a5f2a17f01d0aafa765fd84b94b591e | |
parent | 913412e0c540a0d9f119e5575613162f2095d00c (diff) | |
download | serenity-849495915b0084774a0b3d5a0e05f01434f79aa5.zip |
LibJS: Make Function.prototype a callable function object
20.2.3 Properties of the Function Prototype Object
https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object
The Function prototype object:
- is itself a built-in function object.
3 files changed, 45 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp index e1fbd55190..2fab7b2f4f 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp @@ -21,14 +21,14 @@ namespace JS { FunctionPrototype::FunctionPrototype(GlobalObject& global_object) - : Object(*global_object.object_prototype()) + : FunctionObject(*global_object.object_prototype()) { } void FunctionPrototype::initialize(GlobalObject& global_object) { auto& vm = this->vm(); - Object::initialize(global_object); + Base::initialize(global_object); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(vm.names.apply, apply, 2, attr); define_native_function(vm.names.bind, bind, 1, attr); @@ -39,6 +39,13 @@ void FunctionPrototype::initialize(GlobalObject& global_object) define_direct_property(vm.names.name, js_string(heap(), ""), Attribute::Configurable); } +ThrowCompletionOr<Value> FunctionPrototype::internal_call(Value, MarkedVector<Value>) +{ + // The Function prototype object: + // - accepts any arguments and returns undefined when invoked. + return js_undefined(); +} + // 20.2.3.1 Function.prototype.apply ( thisArg, argArray ), https://tc39.es/ecma262/#sec-function.prototype.apply JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply) { diff --git a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h index 1603f96068..ffaa42a0ed 100644 --- a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Linus Groh <linusg@serenityos.org> + * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,20 +10,27 @@ namespace JS { -class FunctionPrototype final : public Object { - JS_OBJECT(FunctionPrototype, Object); +class FunctionPrototype final : public FunctionObject { + JS_OBJECT(FunctionPrototype, FunctionObject); public: explicit FunctionPrototype(GlobalObject&); virtual void initialize(GlobalObject&) override; virtual ~FunctionPrototype() override = default; + virtual ThrowCompletionOr<Value> internal_call(Value this_argument, MarkedVector<Value> arguments_list) override; + virtual FlyString const& name() const override { return m_name; } + private: JS_DECLARE_NATIVE_FUNCTION(apply); JS_DECLARE_NATIVE_FUNCTION(bind); JS_DECLARE_NATIVE_FUNCTION(call); JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_FUNCTION(symbol_has_instance); + + // Totally unnecessary, but sadly still necessary. + // TODO: Get rid of the pointless name() method. + FlyString m_name { "FunctionPrototype" }; }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Function/Function.prototype.js b/Userland/Libraries/LibJS/Tests/builtins/Function/Function.prototype.js new file mode 100644 index 0000000000..fe3a8ce4bb --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Function/Function.prototype.js @@ -0,0 +1,26 @@ +describe("correct behavior", () => { + test("length is 0", () => { + expect(Function.prototype).toHaveLength(0); + }); + + test("name is empty string", () => { + expect(Function.prototype.name).toBe(""); + }); + + test("basic functionality", () => { + function fn() {} + expect(Object.getPrototypeOf(fn)).toBe(Function.prototype); + expect(Object.getPrototypeOf(Function.prototype)).toBe(Object.prototype); + }); + + test("is callable", () => { + expect(Function.prototype()).toBeUndefined(); + }); + + test("is not constructable", () => { + expect(() => new Function.prototype()).toThrowWithMessage( + TypeError, + "[object FunctionPrototype] is not a constructor (evaluated from 'Function.prototype')" + ); + }); +}); |