From 6e81a8e16c025b2a86b263fb52447485b1655838 Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Thu, 20 Feb 2020 15:59:39 +0000 Subject: [PATCH] function objects --- boa/src/builtins/function_object.rs | 157 ++++++++++++++++++ boa/src/builtins/mod.rs | 2 + .../builtins/object/internal_methods_trait.rs | 108 +++++++++++- boa/src/builtins/object/mod.rs | 126 -------------- boa/src/builtins/value/mod.rs | 23 ++- boa/src/exec/mod.rs | 3 +- 6 files changed, 289 insertions(+), 130 deletions(-) create mode 100644 boa/src/builtins/function_object.rs diff --git a/boa/src/builtins/function_object.rs b/boa/src/builtins/function_object.rs new file mode 100644 index 00000000000..f90d4b3e2bf --- /dev/null +++ b/boa/src/builtins/function_object.rs @@ -0,0 +1,157 @@ +use crate::builtins::object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}; +use crate::builtins::property::Property; +use crate::builtins::value::{same_value, to_value, Value, ValueData}; +use gc::Gc; +use gc_derive::{Finalize, Trace}; +use std::collections::HashMap; +/// 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 { + Base, + Derived, +} +/// Defines how this references are interpreted within the formal parameters and code body of the function. +#[derive(Debug, Copy, Clone)] +pub enum ThisMode { + Lexical, + Strict, + Global, +} +/// Boa representation of a Function Object. +/// +#[derive(Trace, Finalize, Debug, Clone)] +pub struct Function { + /// Kind, this *may* not be needed but will keep for now + pub kind: ObjectKind, + /// Internal Slots + pub internal_slots: Box>, + /// Properties + pub properties: Box>, + // Function Kind + pub function_kind: FunctionKind, + // is constructor?? + pub is_constructor: bool, +} + +impl Function { + // https://tc39.es/ecma262/#sec-functionallocate + pub fn allocate(proto: Value, 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, + } + + let mut func = Function { + kind: ObjectKind::Function, + internal_slots: Box::new(HashMap::new()), + properties: Box::new(HashMap::new()), + function_kind: kind, + is_constructor: needs_construct, + }; + + func.set_internal_slot("extensible", to_value(true)); + func.set_internal_slot(PROTOTYPE, to_value(proto.clone())); + // TODO: set to current realm record + func + } +} + +impl ObjectInternalMethods for Function { + /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v + fn set_prototype_of(&mut self, val: Value) -> bool { + debug_assert!(val.is_object() || val.is_null()); + let current = self.get_internal_slot(PROTOTYPE); + if current == val { + return true; + } + let extensible = self.get_internal_slot("extensible"); + if extensible.is_null() { + return false; + } + let mut p = val.clone(); + let mut done = false; + while !done { + if p.is_null() { + done = true + } else if same_value(&to_value(self.clone()), &p, false) { + return false; + } else { + p = p.get_internal_slot(PROTOTYPE); + } + } + self.set_internal_slot(PROTOTYPE, val); + true + } + + /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p + /// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here. + fn get_own_property(&self, prop: &Value) -> Property { + debug_assert!(Property::is_property_key(prop)); + match self.properties.get(&prop.to_string()) { + // If O does not have an own property with key P, return undefined. + // In this case we return a new empty Property + None => Property::default(), + Some(ref v) => { + let mut d = Property::default(); + if v.is_data_descriptor() { + d.value = v.value.clone(); + d.writable = v.writable; + } else { + debug_assert!(v.is_accessor_descriptor()); + d.get = v.get.clone(); + d.set = v.set.clone(); + } + d.enumerable = v.enumerable; + d.configurable = v.configurable; + d + } + } + } + + /// Insert property into properties hashmap + fn insert_property(&mut self, name: String, p: Property) { + self.properties.insert(name, p); + } + + /// Remove property from properties hashmap + fn remove_property(&mut self, name: &str) { + self.properties.remove(&name.to_string()); + } + + /// Utility function to get an immutable internal slot or Null + fn get_internal_slot(&self, name: &str) -> Value { + match self.internal_slots.get(name) { + Some(v) => v.clone(), + None => Gc::new(ValueData::Null), + } + } + + /// Utility function to set an internal slot + fn set_internal_slot(&mut self, name: &str, val: Value) { + self.internal_slots.insert(name.to_string(), val); + } +} + +/// Function Prototype +/// +pub fn create_function_prototype() { + let mut function_prototype: Object = Object::default(); + // Set Kind to function + function_prototype.kind = ObjectKind::Function; +} diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 18e79c7b0b4..e10a840e522 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -23,6 +23,8 @@ pub mod console; pub mod error; /// The global `Function` object and function value representations pub mod function; +/// The new global `Function` object and function value representations +pub mod function_object; /// The global `JSON` object pub mod json; /// The global `Math` object diff --git a/boa/src/builtins/object/internal_methods_trait.rs b/boa/src/builtins/object/internal_methods_trait.rs index a2a1b47afc9..5c842198470 100644 --- a/boa/src/builtins/object/internal_methods_trait.rs +++ b/boa/src/builtins/object/internal_methods_trait.rs @@ -1,3 +1,4 @@ +use crate::builtins::value::same_value; use crate::builtins::{ object::{Object, PROTOTYPE}, property::Property, @@ -141,6 +142,111 @@ pub trait ObjectInternalMethods { } } + #[allow(clippy::option_unwrap_used)] + fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { + let mut current = self.get_own_property(&to_value(property_key.to_string())); + let extensible = self.is_extensible(); + + // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor + // There currently isn't a property, lets create a new one + if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() { + if !extensible { + return false; + } + + self.insert_property(property_key, desc); + return true; + } + // If every field is absent we don't need to set anything + if desc.is_none() { + return true; + } + + // 4 + if !current.configurable.unwrap_or(false) { + if desc.configurable.is_some() && desc.configurable.unwrap() { + return false; + } + + if desc.enumerable.is_some() + && (desc.enumerable.as_ref().unwrap() != current.enumerable.as_ref().unwrap()) + { + return false; + } + } + + // 5 + if desc.is_generic_descriptor() { + // 6 + } else if current.is_data_descriptor() != desc.is_data_descriptor() { + // a + if !current.configurable.unwrap() { + return false; + } + // b + if current.is_data_descriptor() { + // Convert to accessor + current.value = None; + current.writable = None; + } else { + // c + // convert to data + current.get = None; + current.set = None; + } + + self.insert_property(property_key.clone(), current.clone()); + // 7 + } else if current.is_data_descriptor() && desc.is_data_descriptor() { + // a + if !current.configurable.unwrap() && !current.writable.unwrap() { + if desc.writable.is_some() && desc.writable.unwrap() { + return false; + } + + if desc.value.is_some() + && !same_value( + &desc.value.clone().unwrap(), + ¤t.value.clone().unwrap(), + false, + ) + { + return false; + } + + return true; + } + // 8 + } else { + if !current.configurable.unwrap() { + if desc.set.is_some() + && !same_value( + &desc.set.clone().unwrap(), + ¤t.set.clone().unwrap(), + false, + ) + { + return false; + } + + if desc.get.is_some() + && !same_value( + &desc.get.clone().unwrap(), + ¤t.get.clone().unwrap(), + false, + ) + { + return false; + } + } + + return true; + } + // 9 + self.insert_property(property_key, desc); + true + } + /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p /// The specification returns a Property Descriptor or Undefined. These are 2 separate types and we can't do that here. fn get_own_property(&self, prop: &Value) -> Property; @@ -154,8 +260,6 @@ pub trait ObjectInternalMethods { self.get_internal_slot(PROTOTYPE) } - fn define_own_property(&mut self, property_key: String, desc: Property) -> bool; - /// Utility function to get an immutable internal slot or Null fn get_internal_slot(&self, name: &str) -> Value; diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index f6b09d4f601..762c86597d7 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -142,132 +142,6 @@ impl ObjectInternalMethods for Object { _ => Property::default(), } } - - #[allow(clippy::option_unwrap_used)] - fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { - let mut current = self.get_own_property(&to_value(property_key.to_string())); - let extensible = self.is_extensible(); - - // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor - // There currently isn't a property, lets create a new one - if current.value.is_none() || current.value.as_ref().expect("failed").is_undefined() { - if !extensible { - return false; - } - if desc.value.is_some() && desc.value.clone().unwrap().is_symbol() { - let sym_id = desc - .value - .clone() - .unwrap() - .to_string() - .parse::() - .expect("parsing failed"); - self.sym_properties.insert(sym_id, desc); - } else { - self.properties.insert(property_key, desc); - } - return true; - } - // If every field is absent we don't need to set anything - if desc.is_none() { - return true; - } - - // 4 - if !current.configurable.unwrap_or(false) { - if desc.configurable.is_some() && desc.configurable.unwrap() { - return false; - } - - if desc.enumerable.is_some() - && (desc.enumerable.as_ref().unwrap() != current.enumerable.as_ref().unwrap()) - { - return false; - } - } - - // 5 - if desc.is_generic_descriptor() { - // 6 - } else if current.is_data_descriptor() != desc.is_data_descriptor() { - // a - if !current.configurable.unwrap() { - return false; - } - // b - if current.is_data_descriptor() { - // Convert to accessor - current.value = None; - current.writable = None; - } else { - // c - // convert to data - current.get = None; - current.set = None; - } - - if current.value.is_some() && current.value.clone().unwrap().is_symbol() { - let sym_id = current - .value - .clone() - .unwrap() - .to_string() - .parse::() - .expect("parsing failed"); - self.sym_properties.insert(sym_id, current); - } else { - self.properties.insert(property_key.clone(), current); - } - // 7 - } else if current.is_data_descriptor() && desc.is_data_descriptor() { - // a - if !current.configurable.unwrap() && !current.writable.unwrap() { - if desc.writable.is_some() && desc.writable.unwrap() { - return false; - } - - if desc.value.is_some() - && !same_value( - &desc.value.clone().unwrap(), - ¤t.value.clone().unwrap(), - false, - ) - { - return false; - } - - return true; - } - // 8 - } else { - if !current.configurable.unwrap() { - if desc.set.is_some() - && !same_value( - &desc.set.clone().unwrap(), - ¤t.set.clone().unwrap(), - false, - ) - { - return false; - } - - if desc.get.is_some() - && !same_value( - &desc.get.clone().unwrap(), - ¤t.get.clone().unwrap(), - false, - ) - { - return false; - } - } - - return true; - } - // 9 - self.properties.insert(property_key, desc); - true - } } impl Object { diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index f9a9050098e..68ec3f868c6 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -3,6 +3,7 @@ mod tests; use crate::builtins::{ function::{Function, NativeFunction, NativeFunctionData}, + function_object::Function as FunctionObj, object::{ internal_methods_trait::ObjectInternalMethods, InternalState, InternalStateCell, Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, @@ -50,6 +51,7 @@ pub enum ValueData { Object(GcCell), /// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object Function(Box>), + FunctionObj(Box), /// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots Symbol(GcCell), } @@ -188,6 +190,7 @@ impl ValueData { ValueData::Object(_) | ValueData::Symbol(_) | ValueData::Undefined + | ValueData::FunctionObj(_) | ValueData::Function(_) => NAN, ValueData::String(ref str) => match FromStr::from_str(str) { Ok(num) => num, @@ -208,6 +211,7 @@ impl ValueData { | ValueData::Symbol(_) | ValueData::Null | ValueData::Boolean(false) + | ValueData::FunctionObj(_) | ValueData::Function(_) => 0, ValueData::String(ref str) => match FromStr::from_str(str) { Ok(num) => num, @@ -587,6 +591,7 @@ impl ValueData { ValueData::Null | ValueData::Symbol(_) | ValueData::Undefined + | ValueData::FunctionObj(_) | ValueData::Function(_) => JSONValue::Null, ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Object(ref obj) => { @@ -616,7 +621,7 @@ impl ValueData { ValueData::Symbol(_) => "symbol", ValueData::Null => "null", ValueData::Undefined => "undefined", - ValueData::Function(_) => "function", + ValueData::FunctionObj(_) | ValueData::Function(_) => "function", ValueData::Object(ref o) => { if o.deref().borrow().get_internal_slot("call").is_null() { "object" @@ -852,6 +857,7 @@ impl Display for ValueData { ), ValueData::Object(_) => write!(f, "{}", log_string_from(self, true)), ValueData::Integer(v) => write!(f, "{}", v), + ValueData::FunctionObj(_) => write!(f, "function() {{}}"), ValueData::Function(ref v) => match *v.borrow() { Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"), Function::RegularFunc(ref rf) => { @@ -1122,6 +1128,21 @@ impl FromValue for Object { } } +impl ToValue for FunctionObj { + fn to_value(&self) -> Value { + Gc::new(ValueData::FunctionObj(Box::new(self.clone()))) + } +} + +impl FromValue for FunctionObj { + fn from_value(v: Value) -> Result { + match *v { + ValueData::FunctionObj(ref func) => Ok(*func.clone()), + _ => Err("Value is not a valid object"), + } + } +} + impl ToValue for JSONValue { fn to_value(&self) -> Value { Gc::new(ValueData::from_json(self.clone())) diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 231e9281322..585ac4f4438 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -507,7 +507,7 @@ impl Executor for Interpreter { ValueData::Boolean(_) => "boolean", ValueData::Number(_) | ValueData::Integer(_) => "number", ValueData::String(_) => "string", - ValueData::Function(_) => "function", + ValueData::FunctionObj(_) | ValueData::Function(_) => "function", })) } } @@ -683,6 +683,7 @@ impl Interpreter { pub fn to_object(&mut self, value: &Value) -> ResultValue { match *value.deref().borrow() { ValueData::Undefined + | ValueData::FunctionObj(_) | ValueData::Function(_) | ValueData::Integer(_) | ValueData::Null => Err(Gc::new(ValueData::Undefined)),