Skip to content

Commit

Permalink
Merge e38ecc3 into 461069c
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored Jul 28, 2021
2 parents 461069c + e38ecc3 commit 71e3dd0
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 129 deletions.
19 changes: 19 additions & 0 deletions boa/examples/closures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use boa::{Context, JsString};

fn main() {
let mut context = Context::new();

let variable = JsString::new("I am a captured variable");

context
.register_global_closure("closure", 0, move |_, _, _| {
// This value is captured from main function.
Ok(variable.clone().into())
})
.unwrap();

assert_eq!(
context.eval("closure()"),
Ok("I am an captured variable".into())
);
}
6 changes: 2 additions & 4 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
78 changes: 40 additions & 38 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -52,31 +52,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 +78,17 @@ 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 {
#[allow(clippy::type_complexity)]
function: Box<dyn Fn(&Value, &[Value], &mut Context) -> Result<Value>>,
constructable: bool,
},
Ordinary {
flags: FunctionFlags,
body: RcStatementList,
Expand All @@ -108,6 +97,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 +161,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 +230,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 +273,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 +345,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
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
22 changes: 20 additions & 2 deletions boa/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,10 +543,28 @@ 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(())
}

#[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();

Expand Down
20 changes: 11 additions & 9 deletions boa/src/object/gcobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
use super::{NativeObject, Object, PROTOTYPE};
use crate::{
builtins::function::{
create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction,
},
builtins::function::{create_unmapped_arguments_object, Function, NativeFunction},
context::StandardConstructor,
environment::{
environment_record_trait::EnvironmentRecordTrait,
Expand Down Expand Up @@ -139,17 +137,21 @@ 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, .. } => {
return (function)(this_target, args, context);
}
Function::Ordinary {
body,
params,
Expand Down
Loading

0 comments on commit 71e3dd0

Please sign in to comment.