diff --git a/boa/examples/closures.rs b/boa/examples/closures.rs new file mode 100644 index 00000000000..37f7c355aec --- /dev/null +++ b/boa/examples/closures.rs @@ -0,0 +1,20 @@ +use boa::{Context, JsString, Value}; + +fn main() -> Result<(), Value> { + let mut context = Context::new(); + + let variable = JsString::new("I am a captured variable"); + + // We register a global closure function that has the name 'closure' with length 0. + context.register_global_closure("closure", 0, move |_, _, _| { + // This value is captured from main function. + Ok(variable.clone().into()) + })?; + + assert_eq!( + context.eval("closure()")?, + "I am a captured variable".into() + ); + + Ok(()) +} diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index daaa5d10865..662ff5f5c4e 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -45,16 +45,14 @@ impl BuiltIn for Array { let symbol_iterator = WellKnownSymbols::iterator(); - let get_species = FunctionBuilder::new(context, Self::get_species) + let get_species = FunctionBuilder::native(context, Self::get_species) .name("get [Symbol.species]") .constructable(false) - .callable(true) .build(); - let values_function = FunctionBuilder::new(context, Self::values) + let values_function = FunctionBuilder::native(context, Self::values) .name("values") .length(0) - .callable(true) .constructable(false) .build(); @@ -166,7 +164,7 @@ impl Array { // i. Let intLen be ! ToUint32(len). let int_len = len.to_u32(context).unwrap(); // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. - if !Value::same_value_zero(&int_len.into(), &len) { + if !Value::same_value_zero(&int_len.into(), len) { return Err(context.construct_range_error("invalid array length")); } int_len diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index c8d2cd3a1e6..f7a800a17e3 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -15,7 +15,7 @@ use crate::object::PROTOTYPE; use crate::{ builtins::{Array, BuiltIn}, environment::lexical_environment::Environment, - gc::{empty_trace, Finalize, Trace}, + gc::{custom_trace, empty_trace, Finalize, Trace}, object::{ConstructorBuilder, FunctionBuilder, GcObject, Object, ObjectData}, property::{Attribute, DataDescriptor}, syntax::ast::node::{FormalParameter, RcStatementList}, @@ -23,13 +23,17 @@ use crate::{ }; use bitflags::bitflags; use std::fmt::{self, Debug}; +use std::rc::Rc; #[cfg(test)] mod tests; -/// _fn(this, arguments, context) -> ResultValue_ - The signature of a built-in function +/// _fn(this, arguments, context) -> ResultValue_ - The signature of a native built-in function pub type NativeFunction = fn(&Value, &[Value], &mut Context) -> Result; +/// _fn(this, arguments, context) -> ResultValue_ - The signature of a closure built-in function +pub type ClosureFunction = dyn Fn(&Value, &[Value], &mut Context) -> Result; + #[derive(Clone, Copy, Finalize)] pub struct BuiltInFunction(pub(crate) NativeFunction); @@ -52,31 +56,12 @@ impl Debug for BuiltInFunction { bitflags! { #[derive(Finalize, Default)] pub struct FunctionFlags: u8 { - const CALLABLE = 0b0000_0001; const CONSTRUCTABLE = 0b0000_0010; const LEXICAL_THIS_MODE = 0b0000_0100; } } impl FunctionFlags { - pub(crate) fn from_parameters(callable: bool, constructable: bool) -> Self { - let mut flags = Self::default(); - - if callable { - flags |= Self::CALLABLE; - } - if constructable { - flags |= Self::CONSTRUCTABLE; - } - - flags - } - - #[inline] - pub(crate) fn is_callable(&self) -> bool { - self.contains(Self::CALLABLE) - } - #[inline] pub(crate) fn is_constructable(&self) -> bool { self.contains(Self::CONSTRUCTABLE) @@ -97,9 +82,16 @@ unsafe impl Trace for FunctionFlags { /// FunctionBody is specific to this interpreter, it will either be Rust code or JavaScript code (AST Node) /// /// -#[derive(Debug, Clone, Finalize, Trace)] +#[derive(Finalize)] pub enum Function { - BuiltIn(BuiltInFunction, FunctionFlags), + Native { + function: BuiltInFunction, + constructable: bool, + }, + Closure { + function: Rc, + constructable: bool, + }, Ordinary { flags: FunctionFlags, body: RcStatementList, @@ -108,6 +100,24 @@ pub enum Function { }, } +impl Debug for Function { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Function {{ ... }}") + } +} + +unsafe impl Trace for Function { + custom_trace!(this, { + match this { + Function::Native { .. } => {} + Function::Closure { .. } => {} + Function::Ordinary { environment, .. } => { + mark(environment); + } + } + }); +} + impl Function { // Adds the final rest parameters to the Environment as an array pub(crate) fn add_rest_param( @@ -154,18 +164,11 @@ impl Function { .expect("Failed to intialize binding"); } - /// Returns true if the function object is callable. - pub fn is_callable(&self) -> bool { - match self { - Self::BuiltIn(_, flags) => flags.is_callable(), - Self::Ordinary { flags, .. } => flags.is_callable(), - } - } - /// Returns true if the function object is constructable. pub fn is_constructable(&self) -> bool { match self { - Self::BuiltIn(_, flags) => flags.is_constructable(), + Self::Native { constructable, .. } => *constructable, + Self::Closure { constructable, .. } => *constructable, Self::Ordinary { flags, .. } => flags.is_constructable(), } } @@ -230,7 +233,10 @@ pub fn make_builtin_fn( let _timer = BoaProfiler::global().start_event(&format!("make_builtin_fn: {}", &name), "init"); let mut function = Object::function( - Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + Function::Native { + function: function.into(), + constructable: false, + }, interpreter .standard_objects() .function_object() @@ -270,10 +276,10 @@ impl BuiltInFunctionObject { .expect("this should be an object") .set_prototype_instance(prototype.into()); - this.set_data(ObjectData::Function(Function::BuiltIn( - BuiltInFunction(|_, _, _| Ok(Value::undefined())), - FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, - ))); + this.set_data(ObjectData::Function(Function::Native { + function: BuiltInFunction(|_, _, _| Ok(Value::undefined())), + constructable: true, + })); Ok(this) } @@ -342,10 +348,9 @@ impl BuiltIn for BuiltInFunctionObject { let _timer = BoaProfiler::global().start_event("function", "init"); let function_prototype = context.standard_objects().function_object().prototype(); - FunctionBuilder::new(context, Self::prototype) + FunctionBuilder::native(context, Self::prototype) .name("") .length(0) - .callable(true) .constructable(false) .build_function_prototype(&function_prototype); diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 7629ee0d5b6..f2aee3d1388 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -46,16 +46,14 @@ impl BuiltIn for Map { let to_string_tag = WellKnownSymbols::to_string_tag(); let iterator_symbol = WellKnownSymbols::iterator(); - let get_species = FunctionBuilder::new(context, Self::get_species) + let get_species = FunctionBuilder::native(context, Self::get_species) .name("get [Symbol.species]") .constructable(false) - .callable(true) .build(); - let entries_function = FunctionBuilder::new(context, Self::entries) + let entries_function = FunctionBuilder::native(context, Self::entries) .name("entries") .length(0) - .callable(true) .constructable(false) .build(); diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index d0460746e98..e2b6978c352 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -360,7 +360,7 @@ impl Object { /// Define a property in an object pub fn define_property(_: &Value, args: &[Value], context: &mut Context) -> Result { let object = args.get(0).cloned().unwrap_or_else(Value::undefined); - if let Some(mut object) = object.as_object() { + if let Some(object) = object.as_object() { let key = args .get(1) .unwrap_or(&Value::undefined()) diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 2b0cef2025a..60f9c17f8b5 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -75,53 +75,44 @@ impl BuiltIn for RegExp { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let get_species = FunctionBuilder::new(context, Self::get_species) + let get_species = FunctionBuilder::native(context, Self::get_species) .name("get [Symbol.species]") .constructable(false) - .callable(true) .build(); let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE; - let get_global = FunctionBuilder::new(context, Self::get_global) + let get_global = FunctionBuilder::native(context, Self::get_global) .name("get global") .constructable(false) - .callable(true) .build(); - let get_ignore_case = FunctionBuilder::new(context, Self::get_ignore_case) + let get_ignore_case = FunctionBuilder::native(context, Self::get_ignore_case) .name("get ignoreCase") .constructable(false) - .callable(true) .build(); - let get_multiline = FunctionBuilder::new(context, Self::get_multiline) + let get_multiline = FunctionBuilder::native(context, Self::get_multiline) .name("get multiline") .constructable(false) - .callable(true) .build(); - let get_dot_all = FunctionBuilder::new(context, Self::get_dot_all) + let get_dot_all = FunctionBuilder::native(context, Self::get_dot_all) .name("get dotAll") .constructable(false) - .callable(true) .build(); - let get_unicode = FunctionBuilder::new(context, Self::get_unicode) + let get_unicode = FunctionBuilder::native(context, Self::get_unicode) .name("get unicode") .constructable(false) - .callable(true) .build(); - let get_sticky = FunctionBuilder::new(context, Self::get_sticky) + let get_sticky = FunctionBuilder::native(context, Self::get_sticky) .name("get sticky") .constructable(false) - .callable(true) .build(); - let get_flags = FunctionBuilder::new(context, Self::get_flags) + let get_flags = FunctionBuilder::native(context, Self::get_flags) .name("get flags") .constructable(false) - .callable(true) .build(); - let get_source = FunctionBuilder::new(context, Self::get_source) + let get_source = FunctionBuilder::native(context, Self::get_source) .name("get source") .constructable(false) - .callable(true) .build(); let regexp_object = ConstructorBuilder::with_standard_object( context, diff --git a/boa/src/builtins/set/mod.rs b/boa/src/builtins/set/mod.rs index 556d2d7de80..1a52eddf9d3 100644 --- a/boa/src/builtins/set/mod.rs +++ b/boa/src/builtins/set/mod.rs @@ -39,14 +39,12 @@ impl BuiltIn for Set { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let get_species = FunctionBuilder::new(context, Self::get_species) + let get_species = FunctionBuilder::native(context, Self::get_species) .name("get [Symbol.species]") .constructable(false) - .callable(true) .build(); - let size_getter = FunctionBuilder::new(context, Self::size_getter) - .callable(true) + let size_getter = FunctionBuilder::native(context, Self::size_getter) .constructable(false) .name("get size") .build(); @@ -55,10 +53,9 @@ impl BuiltIn for Set { let to_string_tag = WellKnownSymbols::to_string_tag(); - let values_function = FunctionBuilder::new(context, Self::values) + let values_function = FunctionBuilder::native(context, Self::values) .name("values") .length(0) - .callable(true) .constructable(false) .build(); diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 8ae2cf469fd..25685247bec 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -97,10 +97,9 @@ impl BuiltIn for Symbol { let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - let get_description = FunctionBuilder::new(context, Self::get_description) + let get_description = FunctionBuilder::native(context, Self::get_description) .name("get description") .constructable(false) - .callable(true) .build(); let symbol_object = ConstructorBuilder::with_standard_object( diff --git a/boa/src/context.rs b/boa/src/context.rs index b4938eb9ddc..2ce7c401600 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -21,7 +21,7 @@ use crate::{ }, Parser, }, - BoaProfiler, Executable, Result, Value, + BoaProfiler, Executable, JsString, Result, Value, }; #[cfg(feature = "console")] @@ -486,21 +486,24 @@ impl Context { } /// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions - pub(crate) fn create_function( + pub(crate) fn create_function( &mut self, + name: N, params: P, body: B, flags: FunctionFlags, ) -> Result where + N: Into, P: Into>, B: Into, { + let name = name.into(); let function_prototype: Value = self.standard_objects().function_object().prototype().into(); // Every new function has a prototype property pre-made - let proto = Value::new_object(self); + let prototype = self.construct_object(); let params = params.into(); let params_len = params.len(); @@ -511,30 +514,48 @@ impl Context { environment: self.get_current_environment().clone(), }; - let new_func = Object::function(func, function_prototype); - - let val = Value::from(new_func); + let function = GcObject::new(Object::function(func, function_prototype)); // Set constructor field to the newly created Value (function object) - proto.set_field("constructor", val.clone(), false, self)?; + let constructor = DataDescriptor::new( + function.clone(), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); + prototype.define_property_or_throw("constructor", constructor, self)?; - val.set_field(PROTOTYPE, proto, false, self)?; - val.set_field("length", Value::from(params_len), false, self)?; + let prototype = DataDescriptor::new( + prototype, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + function.define_property_or_throw(PROTOTYPE, prototype, self)?; + let length = DataDescriptor::new( + params_len, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); + function.define_property_or_throw("length", length, self)?; + let name = DataDescriptor::new( + name, + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); + function.define_property_or_throw("name", name, self)?; - Ok(val) + Ok(function.into()) } - /// Register a global function. + /// Register a global native function. + /// + /// This is more efficient that creating a closure function, since this does not allocate, + /// it is just a function pointer. /// - /// The function will be both `callable` and `constructable` (call with `new`). + /// The function will be both `constructable` (call with `new`). /// /// The function will be bound to the global object with `writable`, `non-enumerable` /// and `configurable` attributes. The same as when you create a function in JavaScript. /// /// # Note /// - /// If you want to make a function only `callable` or `constructable`, or wish to bind it differently - /// to the global object, you can create the function object with [`FunctionBuilder`](crate::object::FunctionBuilder). + /// If you want to make a function only `constructable`, or wish to bind it differently + /// to the global object, you can create the function object with [`FunctionBuilder`](crate::object::FunctionBuilder::native). /// And bind it to the global object with [`Context::register_global_property`](Context::register_global_property) method. #[inline] pub fn register_global_function( @@ -543,10 +564,40 @@ impl Context { length: usize, body: NativeFunction, ) -> Result<()> { - let function = FunctionBuilder::new(self, body) + let function = FunctionBuilder::native(self, body) + .name(name) + .length(length) + .constructable(true) + .build(); + + self.global_object().insert_property( + name, + function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); + Ok(()) + } + + /// Register a global closure function. + /// + /// The function will be both `constructable` (call with `new`). + /// + /// The function will be bound to the global object with `writable`, `non-enumerable` + /// and `configurable` attributes. The same as when you create a function in JavaScript. + /// + /// # Note + /// + /// If you want to make a function only `constructable`, or wish to bind it differently + /// to the global object, you can create the function object with [`FunctionBuilder`](crate::object::FunctionBuilder::closure). + /// And bind it to the global object with [`Context::register_global_property`](Context::register_global_property) method. + #[inline] + pub fn register_global_closure(&mut self, name: &str, length: usize, body: F) -> Result<()> + where + F: Fn(&Value, &[Value], &mut Context) -> Result + 'static, + { + let function = FunctionBuilder::closure(self, body) .name(name) .length(length) - .callable(true) .constructable(true) .build(); diff --git a/boa/src/object/gcobject.rs b/boa/src/object/gcobject.rs index bc0f7866443..bcb2ef21317 100644 --- a/boa/src/object/gcobject.rs +++ b/boa/src/object/gcobject.rs @@ -5,7 +5,7 @@ use super::{NativeObject, Object, PROTOTYPE}; use crate::{ builtins::function::{ - create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction, + create_unmapped_arguments_object, ClosureFunction, Function, NativeFunction, }, context::StandardConstructor, environment::{ @@ -26,6 +26,7 @@ use std::{ collections::HashMap, error::Error, fmt::{self, Debug, Display}, + rc::Rc, result::Result as StdResult, }; @@ -46,6 +47,7 @@ pub struct GcObject(Gc>); enum FunctionBody { BuiltInFunction(NativeFunction), BuiltInConstructor(NativeFunction), + Closure(Rc), Ordinary(RcStatementList), } @@ -139,17 +141,19 @@ impl GcObject { .display() .to_string(); return context.throw_type_error(format!("{} is not a constructor", name)); - } else if !construct && !function.is_callable() { - return context.throw_type_error("function object is not callable"); } else { match function { - Function::BuiltIn(BuiltInFunction(function), flags) => { - if flags.is_constructable() || construct { - FunctionBody::BuiltInConstructor(*function) + Function::Native { + function, + constructable, + } => { + if *constructable || construct { + FunctionBody::BuiltInConstructor(function.0) } else { - FunctionBody::BuiltInFunction(*function) + FunctionBody::BuiltInFunction(function.0) } } + Function::Closure { function, .. } => FunctionBody::Closure(function.clone()), Function::Ordinary { body, params, @@ -298,6 +302,7 @@ impl GcObject { function(&Value::undefined(), args, context) } FunctionBody::BuiltInFunction(function) => function(this_target, args, context), + FunctionBody::Closure(function) => (function)(this_target, args, context), FunctionBody::Ordinary(body) => { let result = body.run(context); let this = context.get_this_binding(); diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 010079cfeed..c3495ae1f97 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -152,7 +152,7 @@ impl GcObject { /// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow #[inline] pub fn define_property_or_throw( - &mut self, + &self, key: K, desc: P, context: &mut Context, diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index f3248b0b561..ec75c87f897 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -3,7 +3,7 @@ use crate::{ builtins::{ array::array_iterator::ArrayIterator, - function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, + function::{Function, NativeFunction}, map::map_iterator::MapIterator, map::ordered_map::OrderedMap, regexp::regexp_string_iterator::RegExpStringIterator, @@ -22,6 +22,7 @@ use std::{ any::Any, fmt::{self, Debug, Display}, ops::{Deref, DerefMut}, + rc::Rc, }; #[cfg(test)] @@ -264,7 +265,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-iscallable #[inline] pub fn is_callable(&self) -> bool { - matches!(self.data, ObjectData::Function(ref f) if f.is_callable()) + matches!(self.data, ObjectData::Function(_)) } /// It determines if Object is a function object with a `[[Construct]]` internal method. @@ -682,24 +683,40 @@ where #[derive(Debug)] pub struct FunctionBuilder<'context> { context: &'context mut Context, - function: BuiltInFunction, - name: Option, + function: Option, + name: JsString, length: usize, - callable: bool, - constructable: bool, } impl<'context> FunctionBuilder<'context> { - /// Create a new `FunctionBuilder` + /// Create a new `FunctionBuilder` for creating a native function. #[inline] - pub fn new(context: &'context mut Context, function: NativeFunction) -> Self { + pub fn native(context: &'context mut Context, function: NativeFunction) -> Self { Self { context, - function: function.into(), - name: None, + function: Some(Function::Native { + function: function.into(), + constructable: false, + }), + name: JsString::default(), + length: 0, + } + } + + /// Create a new `FunctionBuilder` for creating a closure function. + #[inline] + pub fn closure(context: &'context mut Context, function: F) -> Self + where + F: Fn(&Value, &[Value], &mut Context) -> Result + 'static, + { + Self { + context, + function: Some(Function::Closure { + function: Rc::new(function), + constructable: false, + }), + name: JsString::default(), length: 0, - callable: true, - constructable: false, } } @@ -711,7 +728,7 @@ impl<'context> FunctionBuilder<'context> { where N: AsRef, { - self.name = Some(name.as_ref().into()); + self.name = name.as_ref().into(); self } @@ -726,21 +743,16 @@ impl<'context> FunctionBuilder<'context> { self } - /// Specify the whether the object function object can be called. - /// - /// The default is `true`. - #[inline] - pub fn callable(&mut self, yes: bool) -> &mut Self { - self.callable = yes; - self - } - /// Specify the whether the object function object can be called with `new` keyword. /// /// The default is `false`. #[inline] pub fn constructable(&mut self, yes: bool) -> &mut Self { - self.constructable = yes; + match self.function.as_mut() { + Some(Function::Native { constructable, .. }) => *constructable = yes, + Some(Function::Closure { constructable, .. }) => *constructable = yes, + _ => unreachable!(), + } self } @@ -748,10 +760,7 @@ impl<'context> FunctionBuilder<'context> { #[inline] pub fn build(&mut self) -> GcObject { let mut function = Object::function( - Function::BuiltIn( - self.function, - FunctionFlags::from_parameters(self.callable, self.constructable), - ), + self.function.take().unwrap(), self.context .standard_objects() .function_object() @@ -759,11 +768,7 @@ impl<'context> FunctionBuilder<'context> { .into(), ); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; - if let Some(name) = self.name.take() { - function.insert_property("name", name, attribute); - } else { - function.insert_property("name", "", attribute); - } + function.insert_property("name", self.name.clone(), attribute); function.insert_property("length", self.length, attribute); GcObject::new(function) @@ -772,10 +777,7 @@ impl<'context> FunctionBuilder<'context> { /// Initializes the `Function.prototype` function object. pub(crate) fn build_function_prototype(&mut self, object: &GcObject) { let mut object = object.borrow_mut(); - object.data = ObjectData::Function(Function::BuiltIn( - self.function, - FunctionFlags::from_parameters(self.callable, self.constructable), - )); + object.data = ObjectData::Function(self.function.take().unwrap()); object.set_prototype_instance( self.context .standard_objects() @@ -783,12 +785,8 @@ impl<'context> FunctionBuilder<'context> { .prototype() .into(), ); - let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - if let Some(name) = self.name.take() { - object.insert_property("name", name, attribute); - } else { - object.insert_property("name", "", attribute); - } + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; + object.insert_property("name", self.name.clone(), attribute); object.insert_property("length", self.length, attribute); } } @@ -836,10 +834,9 @@ impl<'context> ObjectInitializer<'context> { B: Into, { let binding = binding.into(); - let function = FunctionBuilder::new(self.context, function) + let function = FunctionBuilder::native(self.context, function) .name(binding.name) .length(length) - .callable(true) .constructable(false) .build(); @@ -876,7 +873,7 @@ pub struct ConstructorBuilder<'context> { constructor_function: NativeFunction, constructor_object: GcObject, prototype: GcObject, - name: Option, + name: JsString, length: usize, callable: bool, constructable: bool, @@ -907,7 +904,7 @@ impl<'context> ConstructorBuilder<'context> { constructor_object: GcObject::new(Object::default()), prototype: GcObject::new(Object::default()), length: 0, - name: None, + name: JsString::default(), callable: true, constructable: true, inherit: None, @@ -926,7 +923,7 @@ impl<'context> ConstructorBuilder<'context> { constructor_object: object.constructor, prototype: object.prototype, length: 0, - name: None, + name: JsString::default(), callable: true, constructable: true, inherit: None, @@ -940,10 +937,9 @@ impl<'context> ConstructorBuilder<'context> { B: Into, { let binding = binding.into(); - let function = FunctionBuilder::new(self.context, function) + let function = FunctionBuilder::native(self.context, function) .name(binding.name) .length(length) - .callable(true) .constructable(false) .build(); @@ -967,10 +963,9 @@ impl<'context> ConstructorBuilder<'context> { B: Into, { let binding = binding.into(); - let function = FunctionBuilder::new(self.context, function) + let function = FunctionBuilder::native(self.context, function) .name(binding.name) .length(length) - .callable(true) .constructable(false) .build(); @@ -1081,7 +1076,7 @@ impl<'context> ConstructorBuilder<'context> { where N: AsRef, { - self.name = Some(name.as_ref().into()); + self.name = name.as_ref().into(); self } @@ -1122,17 +1117,17 @@ impl<'context> ConstructorBuilder<'context> { /// Build the constructor function object. pub fn build(&mut self) -> GcObject { // Create the native function - let function = Function::BuiltIn( - self.constructor_function.into(), - FunctionFlags::from_parameters(self.callable, self.constructable), - ); + let function = Function::Native { + function: self.constructor_function.into(), + constructable: self.constructable, + }; let length = DataDescriptor::new( self.length, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ); let name = DataDescriptor::new( - self.name.take().unwrap_or_else(|| String::from("[object]")), + self.name.clone(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ); diff --git a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs index 5645727d722..162646f4e4f 100644 --- a/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs @@ -74,11 +74,10 @@ impl ArrowFunctionDecl { impl Executable for ArrowFunctionDecl { fn run(&self, context: &mut Context) -> Result { context.create_function( + "", self.params().to_vec(), self.body().to_vec(), - FunctionFlags::CALLABLE - | FunctionFlags::CONSTRUCTABLE - | FunctionFlags::LEXICAL_THIS_MODE, + FunctionFlags::CONSTRUCTABLE | FunctionFlags::LEXICAL_THIS_MODE, ) } } diff --git a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs index 355d0d0fa0f..792980cb6cb 100644 --- a/boa/src/syntax/ast/node/declaration/function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_decl/mod.rs @@ -89,14 +89,12 @@ impl Executable for FunctionDecl { fn run(&self, context: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec"); let val = context.create_function( + self.name(), self.parameters().to_vec(), self.body().to_vec(), - FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, + FunctionFlags::CONSTRUCTABLE, )?; - // Set the name and assign it in the current environment - val.set_field("name", self.name(), false, context)?; - if context.has_binding(self.name()) { context.set_mutable_binding(self.name(), val, true)?; } else { diff --git a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs index d409beb3827..29e83062f09 100644 --- a/boa/src/syntax/ast/node/declaration/function_expr/mod.rs +++ b/boa/src/syntax/ast/node/declaration/function_expr/mod.rs @@ -100,15 +100,12 @@ impl FunctionExpr { impl Executable for FunctionExpr { fn run(&self, context: &mut Context) -> Result { let val = context.create_function( + self.name().unwrap_or(""), self.parameters().to_vec(), self.body().to_vec(), - FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE, + FunctionFlags::CONSTRUCTABLE, )?; - if let Some(name) = self.name() { - val.set_field("name", Value::from(name), false, context)?; - } - Ok(val) } } diff --git a/boa/src/value/operations.rs b/boa/src/value/operations.rs index 87fe39c4138..88040bace20 100644 --- a/boa/src/value/operations.rs +++ b/boa/src/value/operations.rs @@ -11,9 +11,6 @@ impl Value { (Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) + y), (Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)), - (Self::String(ref x), Self::String(ref y)) => Self::string(format!("{}{}", x, y)), - (Self::String(ref x), y) => Self::string(format!("{}{}", x, y.to_string(context)?)), - (x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(context)?, y)), (Self::String(ref x), Self::String(ref y)) => Self::from(JsString::concat(x, y)), (Self::String(ref x), y) => Self::from(JsString::concat(x, y.to_string(context)?)), (x, Self::String(ref y)) => Self::from(JsString::concat(x.to_string(context)?, y)), diff --git a/boa/src/vm/mod.rs b/boa/src/vm/mod.rs index 741f9847ab8..a3f0ddbc12f 100644 --- a/boa/src/vm/mod.rs +++ b/boa/src/vm/mod.rs @@ -289,13 +289,13 @@ impl<'a> Vm<'a> { let value = self.pop(); let name = &self.code.names[index as usize]; - self.context.initialize_binding(&name, value)?; + self.context.initialize_binding(name, value)?; } Opcode::GetName => { let index = self.read::(); let name = &self.code.names[index as usize]; - let value = self.context.get_binding_value(&name)?; + let value = self.context.get_binding_value(name)?; self.push(value); } Opcode::SetName => { @@ -303,16 +303,16 @@ impl<'a> Vm<'a> { let value = self.pop(); let name = &self.code.names[index as usize]; - if self.context.has_binding(&name) { + if self.context.has_binding(name) { // Binding already exists - self.context.set_mutable_binding(&name, value, true)?; + self.context.set_mutable_binding(name, value, true)?; } else { self.context.create_mutable_binding( name.to_string(), true, VariableScope::Function, )?; - self.context.initialize_binding(&name, value)?; + self.context.initialize_binding(name, value)?; } } Opcode::Jump => { diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 82634a69b84..b8be3404396 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -137,7 +137,7 @@ impl Test { Outcome::Positive => { // TODO: implement async and add `harness/doneprintHandle.js` to the includes. - match self.set_up_env(&harness, strict) { + match self.set_up_env(harness, strict) { Ok(mut context) => { let res = context.eval(&self.content.as_ref()); @@ -183,7 +183,7 @@ impl Test { if let Err(e) = parse(&self.content.as_ref(), strict) { (false, format!("Uncaught {}", e)) } else { - match self.set_up_env(&harness, strict) { + match self.set_up_env(harness, strict) { Ok(mut context) => match context.eval(&self.content.as_ref()) { Ok(res) => (false, format!("{}", res.display())), Err(e) => {