Browse Source

Feature `Function.prototype.call` (#805)

pull/813/head
João Borges 4 years ago committed by GitHub
parent
commit
b027a76bea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      boa/src/builtins/function/mod.rs
  2. 49
      boa/src/builtins/function/tests.rs
  3. 17
      boa/src/builtins/object/tests.rs
  4. 7
      boa/src/builtins/regexp/mod.rs

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

@ -312,6 +312,26 @@ impl BuiltInFunctionObject {
fn prototype(_: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
Ok(Value::undefined())
}
/// `Function.prototype.call`
///
/// The call() method invokes self with the first argument as the `this` value.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function.prototype.call
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
fn call(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: Value = args.get(0).cloned().unwrap_or_default();
// TODO?: 3. Perform PrepareForTailCall
let start = if !args.is_empty() { 1 } else { 0 };
context.call(this, &this_arg, &args[start..])
}
}
impl BuiltIn for BuiltInFunctionObject {
@ -339,6 +359,7 @@ impl BuiltIn for BuiltInFunctionObject {
)
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::call, "call", 1)
.build();
(Self::NAME, function_object.into(), Self::attribute())

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

@ -114,3 +114,52 @@ fn function_prototype_length() {
assert!(value.is_number());
assert_eq!(value.as_number().unwrap(), 0.0);
}
#[test]
fn function_prototype_call() {
let mut engine = Context::new();
let func = r#"
let e = new Error()
Object.prototype.toString.call(e)
"#;
let value = forward_val(&mut engine, func).unwrap();
assert!(value.is_string());
assert_eq!(value.as_string().unwrap(), "[object Error]");
}
#[test]
fn function_prototype_call_throw() {
let mut engine = Context::new();
let throw = r#"
let call = Function.prototype.call;
call(call)
"#;
let value = forward_val(&mut engine, throw).unwrap_err();
assert!(value.is_object());
let string = value.to_string(&mut engine).unwrap();
assert!(string.starts_with("TypeError"))
}
#[test]
fn function_prototype_call_multiple_args() {
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.call(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);
}

17
boa/src/builtins/object/tests.rs

@ -161,15 +161,14 @@ fn object_to_string() {
let o = Object();
"#;
eprintln!("{}", forward(&mut ctx, init));
// TODO: need Function.prototype.call to be implemented
// assert_eq!(
// forward(&mut ctx, "Object.prototype.toString.call(u)"),
// "\"[object Undefined]\""
// );
// assert_eq!(
// forward(&mut ctx, "Object.prototype.toString.call(n)"),
// "\"[object Null]\""
// );
assert_eq!(
forward(&mut ctx, "Object.prototype.toString.call(u)"),
"\"[object Undefined]\""
);
assert_eq!(
forward(&mut ctx, "Object.prototype.toString.call(n)"),
"\"[object Null]\""
);
assert_eq!(forward(&mut ctx, "a.toString()"), "\"[object Array]\"");
assert_eq!(forward(&mut ctx, "f.toString()"), "\"[object Function]\"");
assert_eq!(forward(&mut ctx, "e.toString()"), "\"[object Error]\"");

7
boa/src/builtins/regexp/mod.rs

@ -438,12 +438,15 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let (body, flags) = if let Some(object) = this.as_object() {
let regex = object.as_regexp().unwrap();
(regex.original_source.clone(), regex.flags.clone())
} else {
panic!("object is not an object")
return context.throw_type_error(format!(
"Method RegExp.prototype.toString called on incompatible receiver {}",
this.display()
));
};
Ok(Value::from(format!("/{}/{}", body, flags)))
}

Loading…
Cancel
Save