Skip to content

Commit

Permalink
Implement closure functions (#1442)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Jul 30, 2021
1 parent 461069c commit 91f0fe6
Show file tree
Hide file tree
Showing 18 changed files with 228 additions and 178 deletions.
20 changes: 20 additions & 0 deletions boa/examples/closures.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 5 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand Down
83 changes: 44 additions & 39 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,25 @@ 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},
BoaProfiler, Context, Result, Value,
};
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);

Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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(),
}
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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);

Expand Down
6 changes: 2 additions & 4 deletions boa/src/builtins/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
27 changes: 9 additions & 18 deletions boa/src/builtins/regexp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 3 additions & 6 deletions boa/src/builtins/set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();

Expand Down
3 changes: 1 addition & 2 deletions boa/src/builtins/symbol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading

0 comments on commit 91f0fe6

Please sign in to comment.