Browse Source

Implement closure functions (#1442)

pull/1448/head
Halid Odat 3 years ago committed by GitHub
parent
commit
91f0fe62bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      boa/examples/closures.rs
  2. 8
      boa/src/builtins/array/mod.rs
  3. 83
      boa/src/builtins/function/mod.rs
  4. 6
      boa/src/builtins/map/mod.rs
  5. 2
      boa/src/builtins/object/mod.rs
  6. 27
      boa/src/builtins/regexp/mod.rs
  7. 9
      boa/src/builtins/set/mod.rs
  8. 3
      boa/src/builtins/symbol/mod.rs
  9. 83
      boa/src/context.rs
  10. 19
      boa/src/object/gcobject.rs
  11. 2
      boa/src/object/internal_methods.rs
  12. 109
      boa/src/object/mod.rs
  13. 5
      boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs
  14. 6
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  15. 7
      boa/src/syntax/ast/node/declaration/function_expr/mod.rs
  16. 3
      boa/src/value/operations.rs
  17. 10
      boa/src/vm/mod.rs
  18. 4
      boa_tester/src/exec/mod.rs

20
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(())
}

8
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

83
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<Value>;
/// _fn(this, arguments, context) -> ResultValue_ - The signature of a closure built-in function
pub type ClosureFunction = dyn Fn(&Value, &[Value], &mut Context) -> Result<Value>;
#[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)
///
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Debug, Clone, Finalize, Trace)]
#[derive(Finalize)]
pub enum Function {
BuiltIn(BuiltInFunction, FunctionFlags),
Native {
function: BuiltInFunction,
constructable: bool,
},
Closure {
function: Rc<ClosureFunction>,
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<N>(
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);

6
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();

2
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<Value> {
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())

27
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,

9
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();

3
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(

83
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<P, B>(
pub(crate) fn create_function<N, P, B>(
&mut self,
name: N,
params: P,
body: B,
flags: FunctionFlags,
) -> Result<Value>
where
N: Into<JsString>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
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<F>(&mut self, name: &str, length: usize, body: F) -> Result<()>
where
F: Fn(&Value, &[Value], &mut Context) -> Result<Value> + 'static,
{
let function = FunctionBuilder::closure(self, body)
.name(name)
.length(length)
.callable(true)
.constructable(true)
.build();

19
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<GcCell<Object>>);
enum FunctionBody {
BuiltInFunction(NativeFunction),
BuiltInConstructor(NativeFunction),
Closure(Rc<ClosureFunction>),
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();

2
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<K, P>(
&mut self,
&self,
key: K,
desc: P,
context: &mut Context,

109
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<String>,
function: Option<Function>,
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<F>(context: &'context mut Context, function: F) -> Self
where
F: Fn(&Value, &[Value], &mut Context) -> Result<Value, Value> + '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<str>,
{
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<FunctionBinding>,
{
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<String>,
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<FunctionBinding>,
{
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<FunctionBinding>,
{
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<str>,
{
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,
);

5
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<Value> {
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,
)
}
}

6
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<Value> {
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 {

7
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<Value> {
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)
}
}

3
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)),

10
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::<u32>();
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 => {

4
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) => {

Loading…
Cancel
Save