1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:47:35 +00:00

LibJS: Don't coerce this value to an object in Function.prototype.apply

This commit is contained in:
Luke Wilde 2022-02-10 00:00:27 +00:00 committed by Linus Groh
parent 898ad7c682
commit 12231068bd
2 changed files with 38 additions and 5 deletions

View file

@ -46,15 +46,32 @@ FunctionPrototype::~FunctionPrototype()
// 20.2.3.1 Function.prototype.apply ( thisArg, argArray ), https://tc39.es/ecma262/#sec-function.prototype.apply
JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
{
auto* this_object = TRY(vm.this_value(global_object).to_object(global_object));
if (!this_object->is_function())
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObjectOfType, "Function");
auto& function = static_cast<FunctionObject&>(*this_object);
// 1. Let func be the this value.
auto function_value = vm.this_value(global_object);
// 2. If IsCallable(func) is false, throw a TypeError exception.
if (!function_value.is_function())
return vm.throw_completion<TypeError>(global_object, ErrorType::NotAFunction, function_value.to_string_without_side_effects());
auto& function = static_cast<FunctionObject&>(function_value.as_object());
auto this_arg = vm.argument(0);
auto arg_array = vm.argument(1);
if (arg_array.is_nullish())
// 3. If argArray is undefined or null, then
if (arg_array.is_nullish()) {
// FIXME: a. Perform PrepareForTailCall().
// b. Return ? Call(func, thisArg).
return TRY(JS::call(global_object, function, this_arg));
}
// 4. Let argList be ? CreateListFromArrayLike(argArray).
auto arguments = TRY(create_list_from_array_like(global_object, arg_array));
// FIXME: 5. Perform PrepareForTailCall().
// 6. Return ? Call(func, thisArg, argList).
return TRY(JS::call(global_object, function, this_arg, move(arguments)));
}

View file

@ -49,3 +49,19 @@ test("basic functionality", () => {
expect((() => this).apply("foo")).toBe(globalThis);
});
describe("errors", () => {
test("does not accept non-function values", () => {
expect(() => {
Function.prototype.apply.call("foo");
}).toThrowWithMessage(TypeError, "foo is not a function");
expect(() => {
Function.prototype.apply.call(undefined);
}).toThrowWithMessage(TypeError, "undefined is not a function");
expect(() => {
Function.prototype.apply.call(null);
}).toThrowWithMessage(TypeError, "null is not a function");
});
});