Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement closure functions #1442

Merged
merged 4 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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