Skip to content

Commit

Permalink
Implement [[HostDefined]] field on Realms (#2952)
Browse files Browse the repository at this point in the history
- Improve HostDefined field to allow multiple stored types
  • Loading branch information
HalidOdat authored Sep 27, 2023
1 parent 940e603 commit 822634c
Show file tree
Hide file tree
Showing 29 changed files with 512 additions and 123 deletions.
22 changes: 12 additions & 10 deletions boa_cli/src/debug/limits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,24 @@ fn set_recursion(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> Js
}

pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
let get_loop = FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_loop))
.name("get loop")
.length(0)
.build();
let set_loop = FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_loop))
.name("set loop")
.length(1)
.build();
let get_loop =
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_loop))
.name("get loop")
.length(0)
.build();
let set_loop =
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_loop))
.name("set loop")
.length(1)
.build();

let get_recursion =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_recursion))
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_recursion))
.name("get recursion")
.length(0)
.build();
let set_recursion =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_recursion))
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_recursion))
.name("set recursion")
.length(1)
.build();
Expand Down
28 changes: 16 additions & 12 deletions boa_cli/src/debug/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,28 @@ fn set_statistics(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> J
}

pub(super) fn create_object(context: &mut Context<'_>) -> JsObject {
let get_constant_folding =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_constant_folding))
.name("get constantFolding")
.length(0)
.build();
let set_constant_folding =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_constant_folding))
.name("set constantFolding")
.length(1)
.build();
let get_constant_folding = FunctionObjectBuilder::new(
context.realm(),
NativeFunction::from_fn_ptr(get_constant_folding),
)
.name("get constantFolding")
.length(0)
.build();
let set_constant_folding = FunctionObjectBuilder::new(
context.realm(),
NativeFunction::from_fn_ptr(set_constant_folding),
)
.name("set constantFolding")
.length(1)
.build();

let get_statistics =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_statistics))
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_statistics))
.name("get statistics")
.length(0)
.build();
let set_statistics =
FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_statistics))
FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_statistics))
.name("set statistics")
.length(1)
.build();
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/async_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ impl AsyncGenerator {
// 7. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures generator and performs the following steps when called:
// 8. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
let on_fulfilled = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, generator, context| {
let next = {
Expand Down Expand Up @@ -588,7 +588,7 @@ impl AsyncGenerator {
// 9. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures generator and performs the following steps when called:
// 10. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
let on_rejected = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, generator, context| {
let mut generator_borrow_mut = generator.borrow_mut();
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/function/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ fn closure_capture_clone() {
.unwrap();

let func = FunctionObjectBuilder::new(
ctx,
ctx.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, _, captures, context| {
let (string, object) = &captures;
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/intl/collator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ impl Collator {
f
} else {
let bound_compare = FunctionObjectBuilder::new(
context,
context.realm(),
// 10.3.3.1. Collator Compare Functions
// https://tc39.es/ecma402/#sec-collator-compare-functions
NativeFunction::from_copy_closure_with_captures(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ impl AsyncFromSyncIterator {
// that captures done and performs the following steps when called:
// 9. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, "", « »).
let on_fulfilled = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure(move |_this, args, context| {
// a. Return CreateIterResultObject(value, done).
Ok(create_iter_result_object(
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ impl Object {
// 4. Let closure be a new Abstract Closure with parameters (key, value) that captures
// obj and performs the following steps when called:
let closure = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, args, obj, context| {
let key = args.get_or_undefined(0);
Expand Down
22 changes: 11 additions & 11 deletions boa_engine/src/builtins/promise/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ impl PromiseCapability {
// 4. Let executorClosure be a new Abstract Closure with parameters (resolve, reject) that captures promiseCapability and performs the following steps when called:
// 5. Let executor be CreateBuiltinFunction(executorClosure, 2, "", « »).
let executor = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args: &[JsValue], captures, _| {
let mut promise_capability = captures.borrow_mut();
Expand Down Expand Up @@ -595,7 +595,7 @@ impl Promise {
// p. Set onFulfilled.[[Capability]] to resultCapability.
// q. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
let on_fulfilled = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise.all-resolve-element-functions
Expand Down Expand Up @@ -820,7 +820,7 @@ impl Promise {
// q. Set onFulfilled.[[Capability]] to resultCapability.
// r. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
let on_fulfilled = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise.allsettled-resolve-element-functions
Expand Down Expand Up @@ -906,7 +906,7 @@ impl Promise {
// y. Set onRejected.[[Capability]] to resultCapability.
// z. Set onRejected.[[RemainingElements]] to remainingElementsCount.
let on_rejected = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise.allsettled-reject-element-functions
Expand Down Expand Up @@ -1149,7 +1149,7 @@ impl Promise {
// p. Set onRejected.[[Capability]] to resultCapability.
// q. Set onRejected.[[RemainingElements]] to remainingElementsCount.
let on_rejected = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise.any-reject-element-functions
Expand Down Expand Up @@ -1567,7 +1567,7 @@ impl Promise {

// a. Let thenFinallyClosure be a new Abstract Closure with parameters (value) that captures onFinally and C and performs the following steps when called:
let then_finally_closure = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
/// Capture object for the abstract `returnValue` closure.
Expand All @@ -1588,7 +1588,7 @@ impl Promise {

// iii. Let returnValue be a new Abstract Closure with no parameters that captures value and performs the following steps when called:
let return_value = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, _args, captures, _context| {
// 1. Return value.
Expand Down Expand Up @@ -1618,7 +1618,7 @@ impl Promise {

// c. Let catchFinallyClosure be a new Abstract Closure with parameters (reason) that captures onFinally and C and performs the following steps when called:
let catch_finally_closure = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
/// Capture object for the abstract `throwReason` closure.
Expand All @@ -1639,7 +1639,7 @@ impl Promise {

// iii. Let throwReason be a new Abstract Closure with no parameters that captures reason and performs the following steps when called:
let throw_reason = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, _args, captures, _context| {
// 1. Return ThrowCompletion(reason).
Expand Down Expand Up @@ -2020,7 +2020,7 @@ impl Promise {
// 3. Let lengthResolve be the number of non-optional parameters of the function definition in Promise Resolve Functions.
// 4. Let resolve be CreateBuiltinFunction(stepsResolve, lengthResolve, "", « [[Promise]], [[AlreadyResolved]] »).
let resolve = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise-resolve-functions
Expand Down Expand Up @@ -2119,7 +2119,7 @@ impl Promise {
// 8. Let lengthReject be the number of non-optional parameters of the function definition in Promise Reject Functions.
// 9. Let reject be CreateBuiltinFunction(stepsReject, lengthReject, "", « [[Promise]], [[AlreadyResolved]] »).
let reject = FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
// https://tc39.es/ecma262/#sec-promise-reject-functions
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ impl Proxy {
// 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, "", « [[RevocableProxy]] »).
// 4. Set revoker.[[RevocableProxy]] to p.
FunctionObjectBuilder::new(
context,
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_, _, revocable_proxy, _| {
// a. Let F be the active function object.
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ impl<'host> Context<'host> {
length: usize,
body: NativeFunction,
) -> JsResult<()> {
let function = FunctionObjectBuilder::new(self, body)
let function = FunctionObjectBuilder::new(&self.realm, body)
.name(name)
.length(length)
.constructor(true)
Expand Down Expand Up @@ -289,7 +289,7 @@ impl<'host> Context<'host> {
length: usize,
body: NativeFunction,
) -> JsResult<()> {
let function = FunctionObjectBuilder::new(self, body)
let function = FunctionObjectBuilder::new(&self.realm, body)
.name(name)
.length(length)
.constructor(false)
Expand Down
127 changes: 127 additions & 0 deletions boa_engine/src/host_defined.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use std::any::TypeId;

use boa_gc::{GcRef, GcRefCell, GcRefMut};
use boa_macros::{Finalize, Trace};
use rustc_hash::FxHashMap;

use crate::object::NativeObject;

/// Map used to store the host defined objects.
#[doc(hidden)]
type HostDefinedMap = FxHashMap<TypeId, Box<dyn NativeObject>>;

/// This represents a `ECMASCript` specification \[`HostDefined`\] field.
///
/// This allows storing types which are mapped by their [`TypeId`].
#[derive(Default, Trace, Finalize)]
#[allow(missing_debug_implementations)]
pub struct HostDefined {
state: GcRefCell<HostDefinedMap>,
}

impl HostDefined {
/// Insert a type into the [`HostDefined`].
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn insert_default<T: NativeObject + Default>(&self) -> Option<Box<dyn NativeObject>> {
self.state
.borrow_mut()
.insert(TypeId::of::<T>(), Box::<T>::default())
}

/// Insert a type into the [`HostDefined`].
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn insert<T: NativeObject>(&self, value: T) -> Option<Box<dyn NativeObject>> {
self.state
.borrow_mut()
.insert(TypeId::of::<T>(), Box::new(value))
}

/// Check if the [`HostDefined`] has type T.
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed mutably.
#[track_caller]
pub fn has<T: NativeObject>(&self) -> bool {
self.state.borrow().contains_key(&TypeId::of::<T>())
}

/// Remove type T from [`HostDefined`], if it exists.
///
/// Returns [`Some`] with the object if it exits, [`None`] otherwise.
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn remove<T: NativeObject>(&self) -> Option<Box<dyn NativeObject>> {
self.state.borrow_mut().remove(&TypeId::of::<T>())
}

/// Get type T from [`HostDefined`], if it exits.
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn get<T: NativeObject>(&self) -> Option<GcRef<'_, T>> {
let state = self.state.borrow();

state
.get(&TypeId::of::<T>())
.map(Box::as_ref)
.and_then(<dyn NativeObject>::downcast_ref::<T>)?;

Some(GcRef::map(state, |state| {
state
.get(&TypeId::of::<T>())
.map(Box::as_ref)
.and_then(<dyn NativeObject>::downcast_ref::<T>)
.expect("Should not fail")
}))
}

/// Get type T from [`HostDefined`], if it exits.
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn get_mut<T: NativeObject>(&self) -> Option<GcRefMut<'_, HostDefinedMap, T>> {
let mut state = self.state.borrow_mut();

state
.get_mut(&TypeId::of::<T>())
.map(Box::as_mut)
.and_then(<dyn NativeObject>::downcast_mut::<T>)?;

Some(GcRefMut::map(
state,
|state: &mut FxHashMap<TypeId, Box<dyn NativeObject>>| {
state
.get_mut(&TypeId::of::<T>())
.map(Box::as_mut)
.and_then(<dyn NativeObject>::downcast_mut::<T>)
.expect("Should not fail")
},
))
}

/// Clears all the objects.
///
/// # Panics
///
/// Panics if [`HostDefined`] field is borrowed.
#[track_caller]
pub fn clear(&self) {
self.state.borrow_mut().clear();
}
}
Loading

0 comments on commit 822634c

Please sign in to comment.