diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 19458ce4f63..7d45d1019fc 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -15,7 +15,10 @@ use crate::{ }, }; use gc::{Gc, GcCell}; -use std::{borrow::Borrow, ops::Deref}; +use std::{ + borrow::Borrow, + ops::{Deref, DerefMut}, +}; /// An execution engine pub trait Executor { @@ -470,7 +473,17 @@ impl Default for InterpreterBuilder { impl Interpreter { /// https://tc39.es/ecma262/#sec-call fn call(&mut self, f: &Value, v: &Value, arguments_list: Vec) -> ResultValue { + // All functions should be objects, and eventually will be. + // During this transition call will support both native functions and function objects match (*f).deref() { + ValueData::Object(ref obj) => { + let func: Value = obj.borrow_mut().deref_mut().get_internal_slot("call"); + if !func.is_undefined() { + return self.call(&func, v, arguments_list); + } + // TODO: error object should be here + Err(Gc::new(ValueData::Undefined)) + } ValueData::Function(ref inner_func) => match *inner_func.deref().borrow() { Function::NativeFunc(ref ntv) => { let func = ntv.data; diff --git a/src/lib/js/boolean.rs b/src/lib/js/boolean.rs index 3c688785aa3..196284c2a89 100644 --- a/src/lib/js/boolean.rs +++ b/src/lib/js/boolean.rs @@ -100,6 +100,22 @@ mod tests { assert_eq!(boolean_constructor.is_function(), true); } + #[test] + /// Test the correct type is returned from call and construct + fn construct_and_call() { + let mut engine = Executor::new(); + let init = r#" + const one = new Boolean(1); + const zero = Boolean(0); + "#; + forward(&mut engine, init); + let one = forward_val(&mut engine, "one").unwrap(); + let zero = forward_val(&mut engine, "zero").unwrap(); + + assert_eq!(one.is_object(), true); + assert_eq!(zero.is_boolean(), true); + } + #[test] fn constructor_gives_true_instance() { let mut engine = Executor::new(); diff --git a/src/lib/js/string.rs b/src/lib/js/string.rs index 7442c082c16..2ac665d6ec6 100644 --- a/src/lib/js/string.rs +++ b/src/lib/js/string.rs @@ -7,12 +7,13 @@ use crate::{ value::{from_value, to_value, ResultValue, Value, ValueData}, }, }; +use gc::Gc; use std::{ cmp::{max, min}, f64::NAN, }; -/// Create new string +/// Create new string [[Construct]] /// // This gets called when a new String() is created, it's called by exec:346 pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { @@ -33,6 +34,21 @@ pub fn make_string(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultV Ok(this.clone()) } +/// Call new string [[Call]] +/// https://tc39.es/ecma262/#sec-string-constructor-string-value +pub fn call_string(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + let arg = match args.get(0) { + Some(v) => v.clone(), + None => Gc::new(ValueData::Undefined), + }; + + if arg.is_undefined() { + return Ok(to_value("")); + } + + Ok(to_value(arg.to_string())) +} + /// Get a string's length pub fn get_string_length(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str = ctx.value_to_rust_string(this); @@ -654,7 +670,7 @@ pub fn create_constructor(global: &Value) -> Value { string_constructor.set_internal_method("construct", make_string); // Todo: add call internal method (should be easy) // Currently call points to the constructor function, this is wrong - string_constructor.set_internal_method("call", make_string); + string_constructor.set_internal_method("call", call_string); // Create prototype let proto = ValueData::new_obj(Some(global)); @@ -697,7 +713,7 @@ pub fn init(global: &Value) { mod tests { use super::*; use crate::exec::Executor; - use crate::forward; + use crate::{forward, forward_val}; #[test] fn check_string_constructor_is_function() { @@ -747,6 +763,22 @@ mod tests { //assert_eq!(b, String::from("Hello, world! Have a nice day.")); } + #[test] + /// Test the correct type is returned from call and construct + fn construct_and_call() { + let mut engine = Executor::new(); + let init = r#" + const hello = new String('Hello'); + const world = String('world'); + "#; + forward(&mut engine, init); + let hello = forward_val(&mut engine, "hello").unwrap(); + let world = forward_val(&mut engine, "world").unwrap(); + + assert_eq!(hello.is_object(), true); + assert_eq!(world.is_string(), true); + } + #[test] fn repeat() { let mut engine = Executor::new(); diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index e896fb0780b..a0bd6990357 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -139,6 +139,14 @@ impl ValueData { } } + /// Returns true if the value is a boolean + pub fn is_boolean(&self) -> bool { + match *self { + ValueData::Boolean(_) => true, + _ => false, + } + } + /// Returns true if the value is true /// [toBoolean](https://tc39.github.io/ecma262/#sec-toboolean) pub fn is_true(&self) -> bool { diff --git a/tests/js/test.js b/tests/js/test.js index b895dfad009..c990b090bbf 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -1,4 +1,2 @@ -function jason(a, b) { - return arguments[0]; -} -const val = jason(100, 6); +let a = Boolean(0); +typeof a;