Browse Source

Restrict `ClosureFunction` to only `Copy` closures (#1518)

pull/1528/head
jedel1043 3 years ago committed by GitHub
parent
commit
f9a82b4a13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      Cargo.lock
  2. 1
      boa/Cargo.toml
  3. 7
      boa/examples/closures.rs
  4. 58
      boa/src/builtins/function/mod.rs
  5. 2
      boa/src/context.rs
  6. 3
      boa/src/object/gcobject.rs
  7. 7
      boa/src/object/mod.rs

7
Cargo.lock generated

@ -10,6 +10,7 @@ dependencies = [
"boa_unicode", "boa_unicode",
"chrono", "chrono",
"criterion", "criterion",
"dyn-clone",
"fast-float", "fast-float",
"float-cmp", "float-cmp",
"gc", "gc",
@ -358,6 +359,12 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "dyn-clone"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"

1
boa/Cargo.toml

@ -37,6 +37,7 @@ ryu-js = "0.2.1"
chrono = "0.4.19" chrono = "0.4.19"
fast-float = "0.2.0" fast-float = "0.2.0"
unicode-normalization = "0.1.19" unicode-normalization = "0.1.19"
dyn-clone = "1.0.4"
# Optional Dependencies # Optional Dependencies
measureme = { version = "9.1.2", optional = true } measureme = { version = "9.1.2", optional = true }

7
boa/examples/closures.rs

@ -1,14 +1,15 @@
use boa::{Context, JsString, JsValue}; use boa::{Context, JsValue};
fn main() -> Result<(), JsValue> { fn main() -> Result<(), JsValue> {
let mut context = Context::new(); let mut context = Context::new();
let variable = JsString::new("I am a captured variable"); let variable = "I am a captured variable";
// We register a global closure function that has the name 'closure' with length 0. // We register a global closure function that has the name 'closure' with length 0.
context.register_global_closure("closure", 0, move |_, _, _| { context.register_global_closure("closure", 0, move |_, _, _| {
// This value is captured from main function. // This value is captured from main function.
Ok(variable.clone().into()) println!("variable = {}", variable);
Ok(JsValue::new(variable))
})?; })?;
assert_eq!( assert_eq!(

58
boa/src/builtins/function/mod.rs

@ -15,25 +15,54 @@ use crate::object::PROTOTYPE;
use crate::{ use crate::{
builtins::{Array, BuiltIn}, builtins::{Array, BuiltIn},
environment::lexical_environment::Environment, environment::lexical_environment::Environment,
gc::{custom_trace, empty_trace, Finalize, Trace}, gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, FunctionBuilder, JsObject, Object, ObjectData}, object::{ConstructorBuilder, FunctionBuilder, JsObject, Object, ObjectData},
property::{Attribute, PropertyDescriptor}, property::{Attribute, PropertyDescriptor},
syntax::ast::node::{FormalParameter, RcStatementList}, syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, JsResult, JsValue, BoaProfiler, Context, JsResult, JsValue,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use dyn_clone::DynClone;
use sealed::Sealed;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use std::rc::Rc;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
/// _fn(this, arguments, context) -> ResultValue_ - The signature of a native built-in function // Allows restricting closures to only `Copy` ones.
// Used the sealed pattern to disallow external implementations
// of `DynCopy`.
mod sealed {
pub trait Sealed {}
impl<T: Copy> Sealed for T {}
}
pub trait DynCopy: Sealed {}
impl<T: Copy> DynCopy for T {}
/// Type representing a native built-in function a.k.a. function pointer.
///
/// Native functions need to have this signature in order to
/// be callable from Javascript.
pub type NativeFunction = fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>; pub type NativeFunction = fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>;
/// _fn(this, arguments, context) -> ResultValue_ - The signature of a closure built-in function /// Trait representing a native built-in closure.
pub type ClosureFunction = dyn Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>; ///
/// Closures need to have this signature in order to
/// be callable from Javascript, but most of the time the compiler
/// is smart enough to correctly infer the types.
pub trait ClosureFunction:
Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + DynCopy + DynClone + 'static
{
}
// The `Copy` bound automatically infers `DynCopy` and `DynClone`
impl<T> ClosureFunction for T where
T: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + Copy + 'static
{
}
// Allows cloning Box<dyn ClosureFunction>
dyn_clone::clone_trait_object!(ClosureFunction);
#[derive(Clone, Copy, Finalize)] #[derive(Clone, Copy, Finalize)]
pub struct BuiltInFunction(pub(crate) NativeFunction); pub struct BuiltInFunction(pub(crate) NativeFunction);
@ -83,14 +112,15 @@ unsafe impl Trace for FunctionFlags {
/// FunctionBody is specific to this interpreter, it will either be Rust code or JavaScript code (AST Node) /// 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> /// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Finalize)] #[derive(Trace, Finalize)]
pub enum Function { pub enum Function {
Native { Native {
function: BuiltInFunction, function: BuiltInFunction,
constructable: bool, constructable: bool,
}, },
Closure { Closure {
function: Rc<ClosureFunction>, #[unsafe_ignore_trace]
function: Box<dyn ClosureFunction>,
constructable: bool, constructable: bool,
}, },
Ordinary { Ordinary {
@ -107,18 +137,6 @@ impl Debug for Function {
} }
} }
unsafe impl Trace for Function {
custom_trace!(this, {
match this {
Function::Native { .. } => {}
Function::Closure { .. } => {}
Function::Ordinary { environment, .. } => {
mark(environment);
}
}
});
}
impl Function { impl Function {
// Adds the final rest parameters to the Environment as an array // Adds the final rest parameters to the Environment as an array
pub(crate) fn add_rest_param( pub(crate) fn add_rest_param(

2
boa/src/context.rs

@ -635,7 +635,7 @@ impl Context {
#[inline] #[inline]
pub fn register_global_closure<F>(&mut self, name: &str, length: usize, body: F) -> JsResult<()> pub fn register_global_closure<F>(&mut self, name: &str, length: usize, body: F) -> JsResult<()>
where where
F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + 'static, F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + Copy + 'static,
{ {
let function = FunctionBuilder::closure(self, body) let function = FunctionBuilder::closure(self, body)
.name(name) .name(name)

3
boa/src/object/gcobject.rs

@ -25,7 +25,6 @@ use std::{
collections::HashMap, collections::HashMap,
error::Error, error::Error,
fmt::{self, Debug, Display}, fmt::{self, Debug, Display},
rc::Rc,
result::Result as StdResult, result::Result as StdResult,
}; };
@ -46,7 +45,7 @@ pub struct JsObject(Gc<GcCell<Object>>);
enum FunctionBody { enum FunctionBody {
BuiltInFunction(NativeFunction), BuiltInFunction(NativeFunction),
BuiltInConstructor(NativeFunction), BuiltInConstructor(NativeFunction),
Closure(Rc<ClosureFunction>), Closure(Box<dyn ClosureFunction>),
Ordinary(RcStatementList), Ordinary(RcStatementList),
} }

7
boa/src/object/mod.rs

@ -15,13 +15,12 @@ use crate::{
context::StandardConstructor, context::StandardConstructor,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
BoaProfiler, Context, JsBigInt, JsString, JsSymbol, JsValue, BoaProfiler, Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue,
}; };
use std::{ use std::{
any::Any, any::Any,
fmt::{self, Debug, Display}, fmt::{self, Debug, Display},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc,
}; };
#[cfg(test)] #[cfg(test)]
@ -1109,12 +1108,12 @@ impl<'context> FunctionBuilder<'context> {
#[inline] #[inline]
pub fn closure<F>(context: &'context mut Context, function: F) -> Self pub fn closure<F>(context: &'context mut Context, function: F) -> Self
where where
F: Fn(&JsValue, &[JsValue], &mut Context) -> Result<JsValue, JsValue> + 'static, F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + Copy + 'static,
{ {
Self { Self {
context, context,
function: Some(Function::Closure { function: Some(Function::Closure {
function: Rc::new(function), function: Box::new(function),
constructable: false, constructable: false,
}), }),
name: JsString::default(), name: JsString::default(),

Loading…
Cancel
Save