diff --git a/boa/src/builtins/function_object.rs b/boa/src/builtins/function_object.rs index be5ca56a05f..acf6bded26c 100644 --- a/boa/src/builtins/function_object.rs +++ b/boa/src/builtins/function_object.rs @@ -18,16 +18,7 @@ use std::fmt::{self, Debug}; /// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue; -/// Sets the functionKind -#[derive(Trace, Finalize, Debug, Clone)] -pub enum FunctionKind { - Normal, - ClassConstructor, - Generator, - Async, - AsyncGenerator, - NonConstructor, -} + /// Sets the ConstructorKind #[derive(Debug, Copy, Clone)] pub enum ConstructorKind { @@ -67,10 +58,6 @@ pub struct Function { pub internal_slots: Box>, /// Properties pub properties: Box>, - /// Function Kind - pub function_kind: FunctionKind, - /// is constructor?? - pub is_constructor: bool, /// Function Body pub body: FunctionBody, /// Formal Paramaters @@ -78,7 +65,7 @@ pub struct Function { /// This Mode pub this_mode: ThisMode, // Environment - pub environment: Environment, + pub environment: Option, } impl Function { @@ -91,18 +78,40 @@ impl Function { body: FunctionBody, scope: Environment, this_mode: ThisMode, - mut kind: FunctionKind, ) -> Function { - let needs_construct: bool; - match kind { - FunctionKind::Normal => needs_construct = true, - FunctionKind::NonConstructor => { - needs_construct = false; - kind = FunctionKind::Normal; - } - _ => needs_construct = false, - } + // Create length property and set it's value + let length_property = Property::new() + .writable(false) + .enumerable(false) + .configurable(true) + .value(to_value(parameter_list.len())); + + let mut func = Function { + internal_slots: Box::new(HashMap::new()), + properties: Box::new(HashMap::new()), + body, + environment: Some(scope), + params: parameter_list, + this_mode, + }; + func.set_internal_slot("extensible", to_value(true)); + func.set_internal_slot(PROTOTYPE, to_value(proto.clone())); + func.set_internal_slot("home_object", to_value(undefined())); + + func.define_own_property(String::from("length"), length_property); + func + } + + /// This will create a built-in function object + /// + /// + pub fn create_builtin( + proto: Value, + parameter_list: Vec, + body: FunctionBody, + this_mode: ThisMode, + ) -> Function { // Create length property and set it's value let length_property = Property::new() .writable(false) @@ -113,15 +122,14 @@ impl Function { let mut func = Function { internal_slots: Box::new(HashMap::new()), properties: Box::new(HashMap::new()), - function_kind: kind, - is_constructor: needs_construct, body, - environment: scope, + environment: None, params: parameter_list, this_mode, }; func.set_internal_slot("extensible", to_value(true)); + // TODO: The below needs to be a property not internal slot func.set_internal_slot(PROTOTYPE, to_value(proto.clone())); func.set_internal_slot("home_object", to_value(undefined())); @@ -141,8 +149,11 @@ impl Function { ) -> ResultValue { // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment) // - let local_env = - new_function_environment(this.clone(), undefined(), Some(self.environment.clone())); + let local_env = new_function_environment( + this.clone(), + undefined(), + Some(self.environment.as_ref().unwrap().clone()), + ); // Add argument bindings to the function environment for i in 0..self.params.len() { @@ -173,6 +184,60 @@ impl Function { FunctionBody::Ordinary(ref body) => interpreter.run(body), }; + // local_env gets dropped here, its no longer needed + interpreter.realm.environment.pop(); + result + } + + /// This will handle calls for both ordinary and built-in functions + /// + /// + pub fn construct( + &self, + this: &Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object) + new_target: Value, // new `this` value + args_list: &Vec, + interpreter: &mut Interpreter, + ) -> ResultValue { + // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment) + // + + // builtin constructs functions don't need a new env + let local_env = new_function_environment( + this.clone(), + new_target.clone(), + Some(self.environment.as_ref().unwrap().clone()), + ); + + // Add argument bindings to the function environment + for i in 0..self.params.len() { + let param = self.params.get(i).expect("Could not get param"); + // Rest Parameters + if param.is_rest_param { + self.add_rest_param(param, i, args_list, interpreter, &local_env); + break; + } + + let value = args_list.get(i).expect("Could not get value"); + self.add_arguments_to_environment(param, value.clone(), &local_env); + } + + // Add arguments object + let arguments_obj = create_unmapped_arguments_object(args_list); + local_env + .borrow_mut() + .create_mutable_binding("arguments".to_string(), false); + local_env + .borrow_mut() + .initialize_binding("arguments", arguments_obj); + + interpreter.realm.environment.push(local_env); + + let result = match self.body { + FunctionBody::BuiltIn(func) => func(&new_target, args_list, interpreter), + FunctionBody::Ordinary(ref body) => interpreter.run(body), + }; + interpreter.realm.environment.pop(); result } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 4e3ebae4cba..4554cf3ce44 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -3,6 +3,7 @@ mod tests; use crate::{ builtins::{ + function_object::{Function, FunctionBody, ThisMode}, object::{ internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, @@ -57,24 +58,24 @@ pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// pub fn create_constructor(global: &Value) -> Value { - // Create Symbol constructor (or function in Symbol's case) - let mut symbol_constructor = Object::default(); - symbol_constructor.set_internal_method("call", call_symbol); + // symbol_constructor.set_internal_method("call", call_symbol); // Create prototype - let mut symbol_prototype = Object::default(); + let symbol_prototype = ValueData::new_obj(Some(&global)); + + // Create Symbol constructor (or function in Symbol's case) + let symbol_constructor = Function::create_builtin( + symbol_prototype.clone(), + vec![], + FunctionBody::BuiltIn(call_symbol), + ThisMode::NonLexical, + ); // Symbol.prototype[[Prototype]] points to Object.prototype // Symbol Constructor -> Symbol Prototype -> Object Prototype - let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE); - symbol_prototype.set_internal_slot(INSTANCE_PROTOTYPE, object_prototype); - symbol_prototype.set_method("toString", to_string); - - let symbol_prototype_val = to_value(symbol_prototype); let symbol_constructor_value = to_value(symbol_constructor); - symbol_prototype_val.set_field_slice("construcotor", symbol_constructor_value.clone()); - symbol_constructor_value.set_field_slice(PROTOTYPE, symbol_prototype_val); + symbol_prototype.set_field_slice("constructor", symbol_constructor_value.clone()); symbol_constructor_value } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index cf79689d38c..fdac202f52e 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -5,7 +5,7 @@ use crate::{ builtins::{ array, function::{create_unmapped_arguments_object, Function, RegularFunction}, - function_object::{Function as FunctionObject, FunctionBody, FunctionKind, ThisMode}, + function_object::{Function as FunctionObject, FunctionBody, ThisMode}, object::{ internal_methods_trait::ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, @@ -273,7 +273,6 @@ impl Executor for Interpreter { FunctionBody::Ordinary(*expr.clone()), self.realm.environment.get_current_environment().clone(), ThisMode::Lexical, - FunctionKind::Normal, ); let val = Gc::new(ValueData::FunctionObj(GcCell::new(func))); @@ -418,9 +417,12 @@ impl Executor for Interpreter { func_object.borrow().get_field_slice(PROTOTYPE), ); - let construct = func_object.get_internal_slot("construct"); - - match *construct { + match (*func_object).borrow() { + ValueData::FunctionObj(func) => { + func.borrow_mut() + .deref_mut() + .construct(&func_object, this, &v_args, self) + } ValueData::Function(ref inner_func) => match inner_func.clone().into_inner() { Function::NativeFunc(ref ntv) => { let func = ntv.data; @@ -433,7 +435,7 @@ impl Executor for Interpreter { // Create new scope let env = &mut self.realm.environment; env.push(new_function_environment( - construct.clone(), + func_object.get_internal_slot("construct").clone(), this, Some(env.get_current_environment_ref().clone()), ));