Browse Source

Implement Function.prototype.apply (#877)

pull/881/head
George Roman 4 years ago committed by GitHub
parent
commit
0357a8e05b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      boa/src/builtins/function/mod.rs
  2. 49
      boa/src/builtins/function/tests.rs

29
boa/src/builtins/function/mod.rs

@ -332,6 +332,34 @@ impl BuiltInFunctionObject {
let start = if !args.is_empty() { 1 } else { 0 };
context.call(this, &this_arg, &args[start..])
}
/// `Function.prototype.apply`
///
/// The apply() method invokes self with the first argument as the `this` value
/// and the rest of the arguments provided as an array (or an array-like object).
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function.prototype.apply
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
fn apply(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if !this.is_function() {
return context.throw_type_error(format!("{} is not a function", this.display()));
}
let this_arg = args.get(0).cloned().unwrap_or_default();
let arg_array = args.get(1).cloned().unwrap_or_default();
if arg_array.is_null_or_undefined() {
// TODO?: 3.a. PrepareForTailCall
return context.call(this, &this_arg, &[]);
}
let arg_list = context
.extract_array_properties(&arg_array)
.map_err(|()| arg_array)?;
// TODO?: 5. PrepareForTailCall
context.call(this, &this_arg, &arg_list)
}
}
impl BuiltIn for BuiltInFunctionObject {
@ -360,6 +388,7 @@ impl BuiltIn for BuiltInFunctionObject {
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::call, "call", 1)
.method(Self::apply, "apply", 1)
.build();
(Self::NAME, function_object.into(), Self::attribute())

49
boa/src/builtins/function/tests.rs

@ -163,3 +163,52 @@ fn function_prototype_call_multiple_args() {
.unwrap();
assert!(boolean);
}
#[test]
fn function_prototype_apply() {
let mut engine = Context::new();
let init = r#"
const numbers = [6, 7, 3, 4, 2];
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
"#;
forward_val(&mut engine, init).unwrap();
let boolean = forward_val(&mut engine, "max == 7")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut engine, "min == 2")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
}
#[test]
fn function_prototype_apply_on_object() {
let mut engine = Context::new();
let init = r#"
function f(a, b) {
this.a = a;
this.b = b;
}
let o = {a: 0, b: 0};
f.apply(o, [1, 2]);
"#;
forward_val(&mut engine, init).unwrap();
let boolean = forward_val(&mut engine, "o.a == 1")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
let boolean = forward_val(&mut engine, "o.b == 2")
.unwrap()
.as_boolean()
.unwrap();
assert!(boolean);
}

Loading…
Cancel
Save