Skip to content

Commit

Permalink
documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Jul 30, 2021
1 parent 43eda73 commit a465396
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 42 deletions.
21 changes: 11 additions & 10 deletions boa/examples/closures.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use boa::{Context, JsString};
use boa::{Context, JsString, Value};

fn main() {
fn main() -> Result<(), Value> {
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();
// 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()"),
Ok("I am an captured variable".into())
context.eval("closure()")?,
"I am a captured variable".into()
);

Ok(())
}
9 changes: 6 additions & 3 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -85,8 +89,7 @@ pub enum Function {
constructable: bool,
},
Closure {
#[allow(clippy::type_complexity)]
function: Box<dyn Fn(&Value, &[Value], &mut Context) -> Result<Value>>,
function: Rc<ClosureFunction>,
constructable: bool,
},
Ordinary {
Expand Down
23 changes: 19 additions & 4 deletions boa/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,17 +542,20 @@ impl Context {
Ok(function.into())
}

/// Register a global function.
/// Register a global native function.
///
/// The function will be both `callable` and `constructable` (call with `new`).
/// 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 `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(
Expand All @@ -575,6 +578,18 @@ impl Context {
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
Expand Down
11 changes: 7 additions & 4 deletions boa/src/object/gcobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
use super::{NativeObject, Object, PROTOTYPE};
use crate::{
builtins::function::{create_unmapped_arguments_object, Function, NativeFunction},
builtins::function::{
create_unmapped_arguments_object, ClosureFunction, Function, NativeFunction,
},
context::StandardConstructor,
environment::{
environment_record_trait::EnvironmentRecordTrait,
Expand All @@ -24,6 +26,7 @@ use std::{
collections::HashMap,
error::Error,
fmt::{self, Debug, Display},
rc::Rc,
result::Result as StdResult,
};

Expand All @@ -44,6 +47,7 @@ pub struct GcObject(Gc<GcCell<Object>>);
enum FunctionBody {
BuiltInFunction(NativeFunction),
BuiltInConstructor(NativeFunction),
Closure(Rc<ClosureFunction>),
Ordinary(RcStatementList),
}

Expand Down Expand Up @@ -149,9 +153,7 @@ impl GcObject {
FunctionBody::BuiltInFunction(function.0)
}
}
Function::Closure { function, .. } => {
return (function)(this_target, args, context);
}
Function::Closure { function, .. } => FunctionBody::Closure(function.clone()),
Function::Ordinary {
body,
params,
Expand Down Expand Up @@ -300,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();
Expand Down
36 changes: 15 additions & 21 deletions boa/src/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{
any::Any,
fmt::{self, Debug, Display},
ops::{Deref, DerefMut},
rc::Rc,
};

#[cfg(test)]
Expand Down Expand Up @@ -683,12 +684,12 @@ where
pub struct FunctionBuilder<'context> {
context: &'context mut Context,
function: Option<Function>,
name: Option<String>,
name: JsString,
length: usize,
}

impl<'context> FunctionBuilder<'context> {
/// Create a new `FunctionBuilder`
/// Create a new `FunctionBuilder` for creating a native function.
#[inline]
pub fn native(context: &'context mut Context, function: NativeFunction) -> Self {
Self {
Expand All @@ -697,11 +698,12 @@ impl<'context> FunctionBuilder<'context> {
function: function.into(),
constructable: false,
}),
name: None,
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
Expand All @@ -710,10 +712,10 @@ impl<'context> FunctionBuilder<'context> {
Self {
context,
function: Some(Function::Closure {
function: Box::new(function),
function: Rc::new(function),
constructable: false,
}),
name: None,
name: JsString::default(),
length: 0,
}
}
Expand All @@ -726,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
}

Expand Down Expand Up @@ -766,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)
Expand All @@ -788,11 +786,7 @@ impl<'context> FunctionBuilder<'context> {
.into(),
);
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
if let Some(name) = self.name.take() {
object.insert_property("name", name, attribute);
} else {
object.insert_property("name", "", attribute);
}
object.insert_property("name", self.name.clone(), attribute);
object.insert_property("length", self.length, attribute);
}
}
Expand Down Expand Up @@ -879,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,
Expand Down Expand Up @@ -910,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,
Expand All @@ -929,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,
Expand Down Expand Up @@ -1082,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
}

Expand Down Expand Up @@ -1133,7 +1127,7 @@ impl<'context> ConstructorBuilder<'context> {
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,
);

Expand Down

0 comments on commit a465396

Please sign in to comment.