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
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 32 additions & 8 deletions core/cppgc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,38 @@ 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 initialize object wraps correctly, we store the function
// template in OpState with `T`'s TypeId as the key when binding
// because it'll be pretty annoying to propogate `T` generic everywhere.
//
// 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)
Comment on lines +41 to +49
Copy link
Member

Choose a reason for hiding this comment

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

This seems like a good solution. @devsnek thoughts?

{
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)
}

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

let member = unsafe {
v8::cppgc::make_garbage_collected(
Expand All @@ -54,9 +80,8 @@ pub fn make_cppgc_object<'a, T: GarbageCollected + 'static>(
};

unsafe {
v8::Object::wrap::<CPPGC_TAG, CppGcObject<T>>(scope, obj, &member);
v8::Object::wrap::<CPPGC_TAG, CppGcObject<T>>(isolate, obj, &member);
}

obj
}

Expand Down Expand Up @@ -94,7 +119,6 @@ pub fn try_unwrap_cppgc_object<'sc, T: GarbageCollected + 'static>(
let Ok(obj): Result<v8::Local<v8::Object>, _> = val.try_into() else {
return None;
};

if !obj.is_api_wrapper() {
return None;
}
Expand Down
56 changes: 48 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 @@ -22,6 +23,7 @@ use crate::OpDecl;
use crate::OpMetricsFactoryFn;
use crate::OpState;
use crate::SourceMapData;
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 @@ -37,7 +39,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,12 @@ pub fn init_ops(
.fold(0, |ext_ops_count, count| count + ext_ops_count);
let mut ops = Vec::with_capacity(no_of_ops + deno_core_ops.len());

let no_of_methods = extensions
.iter()
.map(|e| e.method_op_count())
.fold(0, |ext_ops_count, count| count + ext_ops_count);
let mut op_methods = Vec::with_capacity(no_of_methods);

// Collect all middlewares - deno_core extension must not have a middleware!
let middlewares: Vec<Box<OpMiddlewareFn>> = extensions
.iter_mut()
Expand Down Expand Up @@ -76,13 +84,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 @@ -121,22 +134,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::with_capacity(op_method_decls.len());

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 @@ -145,12 +160,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));
}

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_ctxs.into_boxed_slice(),
op_method_ctxs.into_boxed_slice(),
)
}

pub fn get_middlewares_and_external_refs(
Expand Down
30 changes: 29 additions & 1 deletion core/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,22 @@ const NOOP_FN: CFunction = CFunction::new(
&CFunctionInfo::new(Type::Void.scalar(), &[], Int64Representation::Number),
);

// Declaration for object wrappers.
#[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 @@ -384,6 +396,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 @@ -447,6 +460,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 @@ -580,6 +596,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 @@ -603,6 +620,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 @@ -621,6 +639,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 @@ -675,6 +694,10 @@ impl Extension {
self.ops.len()
}

pub fn method_op_count(&self) -> usize {
self.objects.len()
}

/// Called at JsRuntime startup to initialize ops in the isolate.
pub fn init_ops(&mut self) -> &[OpDecl] {
if !self.enabled {
Expand All @@ -685,6 +708,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(crate) fn try_borrow_untyped<T: 'static>(&self, t: TypeId) -> Option<&T> {
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 @@ -177,6 +177,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
12 changes: 12 additions & 0 deletions core/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ impl OpMetadata {
}
}

/// Per-object contexts for members.
pub struct OpMethodCtx {
/// TypeId of the wrapped type
pub id: std::any::TypeId,
/// Op context for the constructor
pub constructor: OpCtx,
/// Per-op context for the methods
pub methods: Vec<OpCtx>,
/// Per-op context for the static methods
pub static_methods: Vec<OpCtx>,
}

/// 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