Skip to content

Commit 1a8ee54

Browse files
authored
feat: use cppgc::Ptr (#818)
Uses denoland/rusty_v8#1523 to avoid cppgc::Member overhead. Also move all Persistent logic into cppgc.rs to clean up the macro code. There is done using a new `Ptr` struct which can deref into the inner member of `CppGcObject`. This is structured so as to play a little trick and inline `T` into `CppGcObject<T>` which halves the allocations and enables a future change for getting `&dyn Reference` from deno-managed cppgc objects.
1 parent a7f27e4 commit 1a8ee54

File tree

8 files changed

+136
-153
lines changed

8 files changed

+136
-153
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ deno_ops = { version = "0.169.0", path = "./ops" }
2424
serde_v8 = { version = "0.202.0", path = "./serde_v8" }
2525
deno_core_testing = { path = "./testing" }
2626

27-
v8 = { version = "0.97.0", default-features = false }
27+
v8 = { version = "0.98.0", default-features = false }
2828
deno_ast = { version = "=0.35.3", features = ["transpiling"] }
2929
deno_unsync = "0.3.10"
3030
deno_core_icudata = "0.0.73"

core/cppgc.rs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ const CPPGC_TAG: u16 = 1;
99
#[repr(C)]
1010
struct CppGcObject<T: GarbageCollected> {
1111
tag: TypeId,
12-
member: v8::cppgc::Member<T>,
12+
member: T,
1313
}
1414

1515
impl<T: GarbageCollected> v8::cppgc::GarbageCollected for CppGcObject<T> {
1616
fn trace(&self, visitor: &v8::cppgc::Visitor) {
17-
visitor.trace(&self.member);
17+
self.member.trace(visitor);
1818
}
1919
}
2020

@@ -48,7 +48,7 @@ pub fn make_cppgc_object<'a, T: GarbageCollected + 'static>(
4848
heap,
4949
CppGcObject {
5050
tag: TypeId::of::<T>(),
51-
member: v8::cppgc::make_garbage_collected(heap, t),
51+
member: t,
5252
},
5353
)
5454
};
@@ -60,31 +60,54 @@ pub fn make_cppgc_object<'a, T: GarbageCollected + 'static>(
6060
obj
6161
}
6262

63+
#[doc(hidden)]
64+
pub struct Ptr<T: GarbageCollected> {
65+
inner: v8::cppgc::Ptr<CppGcObject<T>>,
66+
root: Option<v8::cppgc::Persistent<CppGcObject<T>>>,
67+
}
68+
69+
#[doc(hidden)]
70+
impl<T: GarbageCollected> Ptr<T> {
71+
/// If this pointer is used in an async function, it could leave the stack,
72+
/// so this method can be called to root it in the GC and keep the reference
73+
/// valid.
74+
pub fn root(&mut self) {
75+
if self.root.is_none() {
76+
self.root = Some(v8::cppgc::Persistent::new(&self.inner));
77+
}
78+
}
79+
}
80+
81+
impl<T: GarbageCollected> std::ops::Deref for Ptr<T> {
82+
type Target = T;
83+
fn deref(&self) -> &T {
84+
&self.inner.member
85+
}
86+
}
87+
6388
#[doc(hidden)]
6489
#[allow(clippy::needless_lifetimes)]
6590
pub fn try_unwrap_cppgc_object<'sc, T: GarbageCollected + 'static>(
6691
scope: &mut v8::HandleScope<'sc>,
6792
val: v8::Local<'sc, v8::Value>,
68-
) -> v8::cppgc::Member<T> {
93+
) -> Option<Ptr<T>> {
6994
let Ok(obj): Result<v8::Local<v8::Object>, _> = val.try_into() else {
70-
return v8::cppgc::Member::empty();
95+
return None;
7196
};
7297

7398
if !obj.is_api_wrapper() {
74-
return v8::cppgc::Member::empty();
99+
return None;
75100
}
76101

77-
let ptr =
78-
unsafe { v8::Object::unwrap::<CPPGC_TAG, CppGcObject<T>>(scope, obj) };
79-
let Some(obj) = ptr.borrow() else {
80-
return v8::cppgc::Member::empty();
81-
};
102+
let obj =
103+
unsafe { v8::Object::unwrap::<CPPGC_TAG, CppGcObject<T>>(scope, obj) }?;
82104

83105
if obj.tag != TypeId::of::<T>() {
84-
return v8::cppgc::Member::empty();
106+
return None;
85107
}
86108

87-
let mut h = v8::cppgc::Member::empty();
88-
h.set(&obj.member);
89-
h
109+
Some(Ptr {
110+
inner: obj,
111+
root: None,
112+
})
90113
}

ops/op2/dispatch_fast.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -455,12 +455,12 @@ pub(crate) fn generate_dispatch_fast(
455455
generator_state.needs_fast_scope = true;
456456
generator_state.needs_fast_api_callback_options = true;
457457
gs_quote!(generator_state(self_ty, scope, fast_api_callback_options) => {
458-
let self_handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, this.into());
459-
let Some(self_) = self_handle_.borrow() else {
458+
let Some(self_) = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, this.into()) else {
460459
#fast_api_callback_options.fallback = true;
461460
// SAFETY: All fast return types have zero as a valid value
462461
return unsafe { std::mem::zeroed() };
463462
};
463+
let self_ = &*self_;
464464
})
465465
} else {
466466
quote!()
@@ -764,12 +764,12 @@ fn map_v8_fastcall_arg_to_arg(
764764
*needs_fast_scope = true;
765765
*needs_fast_api_callback_options = true;
766766
quote! {
767-
let handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident);
768-
let Some(#arg_ident) = handle_.borrow() else {
767+
let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) else {
769768
#fast_api_callback_options.fallback = true;
770769
// SAFETY: All fast return types have zero as a valid value
771770
return unsafe { std::mem::zeroed() };
772771
};
772+
let #arg_ident = &*#arg_ident;
773773
}
774774
}
775775
Arg::OptionCppGcResource(ty) => {
@@ -779,20 +779,16 @@ fn map_v8_fastcall_arg_to_arg(
779779
*needs_fast_scope = true;
780780
*needs_fast_api_callback_options = true;
781781
quote! {
782-
let mut handle_ = deno_core::v8::cppgc::Member::empty();
783782
let #arg_ident = if #arg_ident.is_null_or_undefined() {
784783
None
784+
} else if let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) {
785+
Some(#arg_ident)
785786
} else {
786-
handle_.set(&deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident));
787-
match handle_.borrow() {
788-
Some(r) => Some(r),
789-
None => {
790-
#fast_api_callback_options.fallback = true;
791-
// SAFETY: All fast return types have zero as a valid value
792-
return unsafe { std::mem::zeroed() };
793-
}
794-
}
787+
#fast_api_callback_options.fallback = true;
788+
// SAFETY: All fast return types have zero as a valid value
789+
return unsafe { std::mem::zeroed() };
795790
};
791+
let #arg_ident = #arg_ident.as_deref();
796792
}
797793
}
798794
_ => quote!(let #arg_ident = #arg_ident as _;),

ops/op2/dispatch_slow.rs

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -257,26 +257,23 @@ pub(crate) fn with_self(
257257
let tokens = if matches!(ret_val, RetVal::Future(_) | RetVal::FutureResult(_))
258258
{
259259
let tokens = gs_quote!(generator_state(self_ty, fn_args, scope) => {
260-
let self_handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, #fn_args.this().into());
261-
if self_handle_.borrow().is_none() {
260+
let Some(mut self_) = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, #fn_args.this().into()) else {
262261
#throw_exception;
263-
}
264-
let mut self_persistent_ = deno_core::v8::cppgc::Persistent::empty();
265-
self_persistent_.set(&self_handle_);
266-
drop(self_handle_);
262+
};
263+
self_.root();
267264
});
268265

269266
generator_state.moves.push(quote! {
270-
let self_ = self_persistent_.borrow().unwrap();
267+
let self_ = &*self_;
271268
});
272269

273270
tokens
274271
} else {
275272
gs_quote!(generator_state(self_ty, fn_args, scope) => {
276-
let self_handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, #fn_args.this().into());
277-
let Some(self_) = self_handle_.borrow() else {
273+
let Some(self_) = deno_core::_ops::try_unwrap_cppgc_object::<#self_ty>(&mut #scope, #fn_args.this().into()) else {
278274
#throw_exception;
279275
};
276+
let self_ = &*self_;
280277
})
281278
};
282279
Ok(tokens)
@@ -582,24 +579,21 @@ pub fn from_arg(
582579
syn::parse_str::<syn::Path>(ty).expect("Failed to reparse state type");
583580
if matches!(ret_val, RetVal::Future(_) | RetVal::FutureResult(_)) {
584581
let tokens = quote! {
585-
let handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident);
586-
if handle_.borrow().is_none() {
582+
let Some(mut #arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) else {
587583
#throw_exception;
588-
}
589-
let mut #arg_ident = deno_core::v8::cppgc::Persistent::empty();
590-
#arg_ident.set(&handle_);
591-
drop(handle_);
584+
};
585+
#arg_ident.root();
592586
};
593587
generator_state.moves.push(quote! {
594-
let #arg_ident = #arg_ident.borrow().unwrap();
588+
let #arg_ident = &*#arg_ident;
595589
});
596590
tokens
597591
} else {
598592
quote! {
599-
let handle_ = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident);
600-
let Some(#arg_ident) = handle_.borrow() else {
593+
let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) else {
601594
#throw_exception;
602595
};
596+
let #arg_ident = &*#arg_ident;
603597
}
604598
}
605599
}
@@ -613,37 +607,30 @@ pub fn from_arg(
613607
if matches!(ret_val, RetVal::Future(_) | RetVal::FutureResult(_)) {
614608
let tokens = quote! {
615609
let #arg_ident = if #arg_ident.is_null_or_undefined() {
616-
deno_core::v8::cppgc::Persistent::empty()
610+
None
611+
} else if let Some(mut #arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) {
612+
#arg_ident.root();
613+
Some(#arg_ident)
617614
} else {
618-
let handle = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident);
619-
if handle.borrow().is_none() {
620-
#throw_exception;
621-
}
622-
let mut persistent = deno_core::v8::cppgc::Persistent::empty();
623-
persistent.set(&handle);
624-
persistent
615+
#throw_exception;
625616
};
626617
};
627618

628619
generator_state.moves.push(quote! {
629-
let #arg_ident = #arg_ident.borrow();
620+
let #arg_ident = #arg_ident.as_deref();
630621
});
631622

632623
tokens
633624
} else {
634625
quote! {
635-
let mut handle_ = deno_core::v8::cppgc::Member::empty();
636626
let #arg_ident = if #arg_ident.is_null_or_undefined() {
637627
None
628+
} else if let Some(#arg_ident) = deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident) {
629+
Some(#arg_ident)
638630
} else {
639-
handle_.set(&deno_core::_ops::try_unwrap_cppgc_object::<#ty>(&mut #scope, #arg_ident));
640-
match handle_.borrow() {
641-
Some(r) => Some(r),
642-
None => {
643-
#throw_exception;
644-
}
645-
}
631+
#throw_exception;
646632
};
633+
let #arg_ident = #arg_ident.as_deref();
647634
}
648635
}
649636
}

ops/op2/test_cases/async/async_cppgc.out

Lines changed: 21 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)