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

op2 object wraps #805

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
35 changes: 30 additions & 5 deletions core/cppgc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,37 @@ pub fn make_cppgc_object<'a, T: GarbageCollected + 'static>(
t: T,
) -> v8::Local<'a, v8::Object> {
let state = JsRuntime::state_from(scope);
let templ =
v8::Local::new(scope, state.cppgc_template.borrow().as_ref().unwrap());
let func = templ.get_function(scope).unwrap();
let obj = func.new_instance(scope, &[]).unwrap();
let opstate = state.op_state.borrow();

// To support initializing object wraps correctly, we store the function
// template in the opstate during binding with `T`'s TypeId as the key
// because it'll be pretty annoying to propogate `T` everywhere.
littledivy marked this conversation as resolved.
Show resolved Hide resolved
//
// Here we try to retrive a function template for `T`, falling back to
// the default cppgc template.
let id = TypeId::of::<T>();
let obj = if let Some(templ) =
opstate.try_borrow_untyped::<v8::Global<v8::FunctionTemplate>>(id)
{
let templ = v8::Local::new(scope, templ);
let inst = templ.instance_template(scope);
inst.new_instance(scope).unwrap()
} else {
let templ =
v8::Local::new(scope, state.cppgc_template.borrow().as_ref().unwrap());
let func = templ.get_function(scope).unwrap();
func.new_instance(scope, &[]).unwrap()
};

wrap_object(scope, obj, t)
}

// Wrap an API object (eg: `args.This()`)
pub fn wrap_object<'a, T: GarbageCollected + 'static>(
scope: &mut v8::HandleScope<'a>,
obj: v8::Local<'a, v8::Object>,
t: T,
) -> v8::Local<'a, v8::Object> {
let heap = scope.get_cpp_heap().unwrap();

let member = unsafe {
Expand All @@ -56,7 +82,6 @@ pub fn make_cppgc_object<'a, T: GarbageCollected + 'static>(
unsafe {
v8::Object::wrap::<CPPGC_TAG, CppGcObject<T>>(scope, obj, &member);
}

obj
}

Expand Down
51 changes: 43 additions & 8 deletions core/extension_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::extensions::GlobalTemplateMiddlewareFn;
use crate::extensions::OpMiddlewareFn;
use crate::modules::ModuleName;
use crate::ops::OpCtx;
use crate::ops::OpMethodCtx;
use crate::runtime::ExtensionTranspiler;
use crate::runtime::JsRuntimeState;
use crate::runtime::OpDriverImpl;
Expand All @@ -23,6 +24,7 @@ use crate::OpDecl;
use crate::OpMetricsFactoryFn;
use crate::OpState;
use crate::SourceMapGetter;
use crate::_ops::OpMethodDecl;

/// Contribute to the `OpState` from each extension.
pub fn setup_op_state(op_state: &mut OpState, extensions: &mut [Extension]) {
Expand All @@ -38,7 +40,7 @@ pub fn setup_op_state(op_state: &mut OpState, extensions: &mut [Extension]) {
pub fn init_ops(
deno_core_ops: &'static [OpDecl],
extensions: &mut [Extension],
) -> Vec<OpDecl> {
) -> (Vec<OpDecl>, Vec<OpMethodDecl>) {
// In debug build verify there that inter-Extension dependencies
// are setup correctly.
#[cfg(debug_assertions)]
Expand All @@ -49,6 +51,7 @@ pub fn init_ops(
.map(|e| e.op_count())
.fold(0, |ext_ops_count, count| count + ext_ops_count);
let mut ops = Vec::with_capacity(no_of_ops + deno_core_ops.len());
let mut op_methods = Vec::new();
littledivy marked this conversation as resolved.
Show resolved Hide resolved

// Collect all middlewares - deno_core extension must not have a middleware!
let middlewares: Vec<Box<OpMiddlewareFn>> = extensions
Expand Down Expand Up @@ -77,13 +80,18 @@ pub fn init_ops(
..macroware(*ext_op)
});
}

let ext_method_ops = ext.init_method_ops();
for ext_op in ext_method_ops {
littledivy marked this conversation as resolved.
Show resolved Hide resolved
op_methods.push(*ext_op);
}
}

// In debug build verify there are no duplicate ops.
#[cfg(debug_assertions)]
check_no_duplicate_op_names(&ops);

ops
(ops, op_methods)
}

/// This functions panics if any of the extensions is missing its dependencies.
Expand Down Expand Up @@ -122,22 +130,24 @@ fn check_no_duplicate_op_names(ops: &[OpDecl]) {

pub fn create_op_ctxs(
op_decls: Vec<OpDecl>,
op_method_decls: Vec<OpMethodDecl>,
op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
op_driver: Rc<OpDriverImpl>,
op_state: Rc<RefCell<OpState>>,
runtime_state: Rc<JsRuntimeState>,
get_error_class_fn: GetErrorClassFn,
) -> Box<[OpCtx]> {
) -> (Box<[OpCtx]>, Box<[OpMethodCtx]>) {
let op_count = op_decls.len();
let mut op_ctxs = Vec::with_capacity(op_count);
let mut op_method_ctxs = vec![];
littledivy marked this conversation as resolved.
Show resolved Hide resolved

let runtime_state_ptr = runtime_state.as_ref() as *const _;
for (index, decl) in op_decls.into_iter().enumerate() {
let create_ctx = |index, decl| {
let metrics_fn = op_metrics_factory_fn
.as_ref()
.and_then(|f| (f)(index as _, op_count, &decl));

let op_ctx = OpCtx::new(
OpCtx::new(
index as _,
std::ptr::null_mut(),
op_driver.clone(),
Expand All @@ -146,12 +156,37 @@ pub fn create_op_ctxs(
runtime_state_ptr,
get_error_class_fn,
metrics_fn,
);
)
};

op_ctxs.push(op_ctx);
for (index, decl) in op_decls.into_iter().enumerate() {
op_ctxs.push(create_ctx(index, decl));
}

op_ctxs.into_boxed_slice()
for (index, mut decl) in op_method_decls.into_iter().enumerate() {
decl.constructor.name = decl.name.0;
decl.constructor.name_fast = decl.name.1;
littledivy marked this conversation as resolved.
Show resolved Hide resolved

op_method_ctxs.push(OpMethodCtx {
id: (decl.id)(),
constructor: create_ctx(index, decl.constructor),
methods: decl
.methods
.iter()
.map(|method_decl| create_ctx(index, *method_decl))
.collect(),
static_methods: decl
.static_methods
.iter()
.map(|method_decl| create_ctx(index, *method_decl))
.collect(),
});
}

(
op_ctxs.into_boxed_slice(),
op_method_ctxs.into_boxed_slice(),
)
}

pub fn get_middlewares_and_external_refs(
Expand Down
26 changes: 25 additions & 1 deletion core/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,22 @@ pub type GlobalObjectMiddlewareFn =

extern "C" fn noop() {}

// Delcration for object wrappers.
littledivy marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Copy)]
pub struct OpMethodDecl {
// TypeId::of::<T>() is unstable-nightly in const context so
// we store the fn pointer instead.
pub id: fn() -> std::any::TypeId,
Comment on lines +182 to +184
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please open an issue to migrate to TypeId::of::<T>() once it becomes stable

pub name: (&'static str, FastStaticString),
pub constructor: OpDecl,
pub methods: &'static [OpDecl],
pub static_methods: &'static [OpDecl],
}

#[derive(Clone, Copy)]
pub struct OpDecl {
pub name: &'static str,
pub(crate) name_fast: FastStaticString,
pub name_fast: FastStaticString,
pub is_async: bool,
pub is_reentrant: bool,
pub arg_count: u8,
Expand Down Expand Up @@ -391,6 +403,7 @@ macro_rules! extension {
$(, bounds = [ $( $bound:path : $bound_type:ident ),+ ] )?
$(, ops_fn = $ops_symbol:ident $( < $ops_param:ident > )? )?
$(, ops = [ $( $(#[$m:meta])* $( $op:ident )::+ $( < $( $op_param:ident ),* > )? ),+ $(,)? ] )?
$(, objects = [ $( $object:ident )::+ ] )?
$(, esm_entry_point = $esm_entry_point:expr )?
$(, esm = [ $($esm:tt)* ] )?
$(, lazy_loaded_esm = [ $($lazy_loaded_esm:tt)* ] )?
Expand Down Expand Up @@ -454,6 +467,9 @@ macro_rules! extension {
$( #[ $m ] )*
$( $op )::+ $( :: < $($op_param),* > )? ()
}),+)?]),
objects: ::std::borrow::Cow::Borrowed(&[
$( $( $object )::+::DECL, )*
]),
external_references: ::std::borrow::Cow::Borrowed(&[ $( $external_reference ),* ]),
global_template_middleware: ::std::option::Option::None,
global_object_middleware: ::std::option::Option::None,
Expand Down Expand Up @@ -587,6 +603,7 @@ pub struct Extension {
pub lazy_loaded_esm_files: Cow<'static, [ExtensionFileSource]>,
pub esm_entry_point: Option<&'static str>,
pub ops: Cow<'static, [OpDecl]>,
pub objects: Cow<'static, [OpMethodDecl]>,
pub external_references: Cow<'static, [v8::ExternalReference<'static>]>,
pub global_template_middleware: Option<GlobalTemplateMiddlewareFn>,
pub global_object_middleware: Option<GlobalObjectMiddlewareFn>,
Expand All @@ -610,6 +627,7 @@ impl Extension {
lazy_loaded_esm_files: Cow::Borrowed(&[]),
esm_entry_point: None,
ops: self.ops.clone(),
objects: self.objects.clone(),
external_references: self.external_references.clone(),
global_template_middleware: self.global_template_middleware,
global_object_middleware: self.global_object_middleware,
Expand All @@ -628,6 +646,7 @@ impl Default for Extension {
lazy_loaded_esm_files: Cow::Borrowed(&[]),
esm_entry_point: None,
ops: Cow::Borrowed(&[]),
objects: Cow::Borrowed(&[]),
external_references: Cow::Borrowed(&[]),
global_template_middleware: None,
global_object_middleware: None,
Expand Down Expand Up @@ -692,6 +711,11 @@ impl Extension {
self.ops.as_ref()
}

/// Called at JsRuntime startup to initialize method ops in the isolate.
pub fn init_method_ops(&self) -> &[OpMethodDecl] {
self.objects.as_ref()
}

/// Allows setting up the initial op-state of an isolate at startup.
pub fn take_state(&mut self, state: &mut OpState) {
if let Some(op_fn) = self.op_state_fn.take() {
Expand Down
10 changes: 10 additions & 0 deletions core/gotham_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ impl GothamState {
self.data.insert(type_id, Box::new(t));
}

// For internal use.
pub(crate) fn put_untyped<T: 'static>(&mut self, t: TypeId, v: T) {
self.data.insert(t, Box::new(v));
}

/// Determines if the current value exists in `GothamState` storage.
pub fn has<T: 'static>(&self) -> bool {
let type_id = TypeId::of::<T>();
Expand All @@ -34,6 +39,11 @@ impl GothamState {
self.data.get(&type_id).and_then(|b| b.downcast_ref())
}

// For internal use.
pub fn try_borrow_untyped<T: 'static>(&self, t: TypeId) -> Option<&T> {
littledivy marked this conversation as resolved.
Show resolved Hide resolved
self.data.get(&t).and_then(|b| b.downcast_ref())
}

/// Borrows a value from the `GothamState` storage.
pub fn borrow<T: 'static>(&self) -> &T {
self.try_borrow().unwrap_or_else(|| missing::<T>())
Expand Down
1 change: 1 addition & 0 deletions core/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ pub mod _ops {
pub use super::error_codes::get_error_code;
pub use super::extensions::Op;
pub use super::extensions::OpDecl;
pub use super::extensions::OpMethodDecl;
#[cfg(debug_assertions)]
pub use super::ops::reentrancy_check;
pub use super::ops::OpCtx;
Expand Down
7 changes: 7 additions & 0 deletions core/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ impl OpMetadata {
}
}

pub struct OpMethodCtx {
pub id: std::any::TypeId,
pub constructor: OpCtx,
pub methods: Vec<OpCtx>,
pub static_methods: Vec<OpCtx>,
}
littledivy marked this conversation as resolved.
Show resolved Hide resolved

/// Per-op context.
///
// Note: We don't worry too much about the size of this struct because it's allocated once per realm, and is
Expand Down
Loading
Loading