/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libs/core/cppgc.rs
589 строк
15 KB
Leo Kettmeir
feat: `deno desktop` subcommand (#33441)
16 июн 2026, 13:41
Не верифицирован
16 июн 2026, 13:41
8398162
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::any::TypeId; use std::any::type_name; use std::collections::BTreeMap; use std::collections::HashMap; use serde::Deserialize; use serde::Serialize; pub use v8::cppgc::GarbageCollected; pub use v8::cppgc::GcCell; use crate::JsRuntime; use crate::runtime::SnapshotLoadDataStore; use crate::runtime::SnapshotStoreDataStore; const CPPGC_SINGLE_TAG: u16 = 1; #[repr(C)] struct CppGcObject<T: GarbageCollected> { tag: TypeId, member: T, } unsafe impl<T: GarbageCollected> v8::cppgc::GarbageCollected for CppGcObject<T> { fn trace(&self, visitor: &mut v8::cppgc::Visitor) { self.member.trace(visitor); } fn get_name(&self) -> &'static std::ffi::CStr { self.member.get_name() } } pub(crate) fn cppgc_template_constructor( _scope: &mut v8::PinScope, _args: v8::FunctionCallbackArguments, _rv: v8::ReturnValue, ) { } pub(crate) fn make_cppgc_template<'s, 'i>( scope: &mut v8::PinScope<'s, 'i, ()>, ) -> v8::Local<'s, v8::FunctionTemplate> { v8::FunctionTemplate::new(scope, cppgc_template_constructor) } #[doc(hidden)] pub fn make_cppgc_empty_object<'a, 'i, T: GarbageCollected + 'static>( scope: &v8::PinScope<'a, 'i>, ) -> v8::Local<'a, v8::Object> { let state = JsRuntime::state_from(scope); let templates = state.function_templates.borrow(); match templates.get::<T>() { Some(templ) => { let templ = v8::Local::new(scope, templ); let inst = templ.instance_template(scope); inst.new_instance(scope).unwrap() } _ => { 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() } } } pub fn make_cppgc_object<'a, 'i, T: GarbageCollected + 'static>( scope: &mut v8::PinScope<'a, 'i>, t: T, ) -> v8::Local<'a, v8::Object> { let obj = make_cppgc_empty_object::<T>(scope); wrap_object(scope, obj, t) } // 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(); unsafe { let member = v8::cppgc::make_garbage_collected( heap, CppGcObject { tag: TypeId::of::<T>(), member: t, }, ); v8::Object::wrap::<CPPGC_SINGLE_TAG, CppGcObject<T>>(isolate, obj, &member); obj } } pub fn make_cppgc_proto_object<'a, 'i, T: GarbageCollected + 'static>( scope: &mut v8::PinScope<'a, 'i>, t: T, ) -> v8::Local<'a, v8::Object> { make_cppgc_object(scope, t) } pub struct UnsafePtr<T: GarbageCollected> { inner: v8::cppgc::UnsafePtr<CppGcObject<T>>, root: Option<v8::cppgc::Persistent<CppGcObject<T>>>, } impl<T: GarbageCollected> UnsafePtr<T> { #[allow(clippy::missing_safety_doc, reason = "internal hidden API")] pub unsafe fn as_ref(&self) -> &T { unsafe { &self.inner.as_ref().member } } } #[doc(hidden)] impl<T: GarbageCollected> UnsafePtr<T> { /// If this pointer is used in an async function, it could leave the stack, /// so this method can be called to root it in the GC and keep the reference /// valid. pub fn root(&mut self) { if self.root.is_none() { self.root = Some(v8::cppgc::Persistent::new(&self.inner)); } } } impl<T: GarbageCollected> std::ops::Deref for UnsafePtr<T> { type Target = T; fn deref(&self) -> &T { &unsafe { self.inner.as_ref() }.member } } #[doc(hidden)] #[allow( clippy::needless_lifetimes, reason = "explicit lifetimes improve clarity" )] fn try_unwrap_cppgc_with<'sc, T: GarbageCollected + 'static>( isolate: &mut v8::Isolate, val: v8::Local<'sc, v8::Value>, inheriting: &[TypeId], ) -> Option<UnsafePtr<T>> { let Ok(obj): Result<v8::Local<v8::Object>, _> = val.try_into() else { return None; }; if !obj.is_api_wrapper() { return None; } let obj = unsafe { v8::Object::unwrap::<CPPGC_SINGLE_TAG, CppGcObject<T>>(isolate, obj) }?; let tag = unsafe { obj.as_ref() }.tag; if tag != TypeId::of::<T>() && !inheriting.contains(&tag) { return None; } Some(UnsafePtr { inner: obj, root: None, }) } #[doc(hidden)] #[allow( clippy::needless_lifetimes, reason = "explicit lifetimes improve clarity" )] pub fn try_unwrap_cppgc_object<'sc, T: GarbageCollected + 'static>( isolate: &mut v8::Isolate, val: v8::Local<'sc, v8::Value>, ) -> Option<UnsafePtr<T>> { try_unwrap_cppgc_with::<T>(isolate, val, &[]) } #[doc(hidden)] #[allow( clippy::needless_lifetimes, reason = "explicit lifetimes improve clarity" )] pub fn try_unwrap_cppgc_base_object< 'sc, T: GarbageCollected + Base + 'static, >( isolate: &mut v8::Isolate, val: v8::Local<'sc, v8::Value>, ) -> Option<UnsafePtr<T>> { try_unwrap_cppgc_with::<T>(isolate, val, T::inheriting_types()) } pub struct Ref<T: GarbageCollected> { inner: v8::cppgc::Persistent<CppGcObject<T>>, } impl<T: GarbageCollected> std::ops::Deref for Ref<T> { type Target = T; fn deref(&self) -> &T { &self.inner.get().unwrap().member } } #[doc(hidden)] #[allow( clippy::needless_lifetimes, reason = "explicit lifetimes improve clarity" )] pub fn try_unwrap_cppgc_persistent_object< 'sc, T: GarbageCollected + 'static, >( isolate: &mut v8::Isolate, val: v8::Local<'sc, v8::Value>, ) -> Option<Ref<T>> { let ptr = try_unwrap_cppgc_object::<T>(isolate, val)?; Some(Ref { inner: v8::cppgc::Persistent::new(&ptr.inner), }) } #[doc(hidden)] #[allow( clippy::needless_lifetimes, reason = "explicit lifetimes improve clarity" )] pub fn try_unwrap_cppgc_base_persistent_object< 'sc, T: GarbageCollected + Base + 'static, >( isolate: &mut v8::Isolate, val: v8::Local<'sc, v8::Value>, ) -> Option<Ref<T>> { let ptr = try_unwrap_cppgc_base_object::<T>(isolate, val)?; Some(Ref { inner: v8::cppgc::Persistent::new(&ptr.inner), }) } pub struct Member<T: GarbageCollected> { inner: v8::cppgc::Member<CppGcObject<T>>, } impl<T: GarbageCollected> From<Ref<T>> for Member<T> { fn from(value: Ref<T>) -> Self { Member { inner: v8::cppgc::Member::new(&value.inner), } } } impl<T: GarbageCollected> std::ops::Deref for Member<T> { type Target = T; fn deref(&self) -> &T { &unsafe { self.inner.get().unwrap() }.member } } impl<T: GarbageCollected> v8::cppgc::Traced for Member<T> { fn trace(&self, visitor: &mut v8::cppgc::Visitor) { visitor.trace(&self.inner); } } #[derive(Default)] pub struct FunctionTemplateData { store: BTreeMap<String, v8::Global<v8::FunctionTemplate>>, } #[derive(Default, Serialize, Deserialize)] pub struct FunctionTemplateSnapshotData { store_handles: Vec<(String, u32)>, } impl FunctionTemplateData { pub fn insert( &mut self, key: String, value: v8::Global<v8::FunctionTemplate>, ) { self.store.insert(key, value); } fn get<T>(&self) -> Option<&v8::Global<v8::FunctionTemplate>> { self.store.get(type_name::<T>()) } pub fn get_raw( &self, key: &str, ) -> Option<&v8::Global<v8::FunctionTemplate>> { self.store.get(key) } pub fn serialize_for_snapshotting( self, data_store: &mut SnapshotStoreDataStore, ) -> FunctionTemplateSnapshotData { FunctionTemplateSnapshotData { store_handles: self .store .into_iter() .map(|(k, v)| (k, data_store.register(v))) .collect(), } } pub fn update_with_snapshotted_data( &mut self, scope: &mut v8::PinScope, data_store: &mut SnapshotLoadDataStore, data: FunctionTemplateSnapshotData, ) { self.store = data .store_handles .into_iter() .map(|(k, v)| (k, data_store.get::<v8::FunctionTemplate>(scope, v))) .collect(); } } #[derive(Debug)] pub struct SameObject<T: GarbageCollected + 'static> { cell: std::cell::OnceCell<v8::Global<v8::Object>>, _phantom_data: std::marker::PhantomData<T>, } impl<T: GarbageCollected + 'static> SameObject<T> { #[allow( clippy::new_without_default, reason = "Default would hide the intentional construction" )] pub fn new() -> Self { Self { cell: Default::default(), _phantom_data: Default::default(), } } pub fn get<F>(&self, scope: &mut v8::PinScope, f: F) -> v8::Global<v8::Object> where F: FnOnce(&mut v8::PinScope) -> T, { self .cell .get_or_init(|| { let v = f(scope); let obj = make_cppgc_object(scope, v); v8::Global::new(scope, obj) }) .clone() } /// Like [`SameObject::get`] but the initializer can fail. On error the /// cache is left empty so a subsequent call can retry. Useful when the /// underlying construction (e.g. wgpu surface, native handle lookup) /// can legitimately fail and we want to bubble the error to JS instead /// of panicking inside the closure. pub fn try_get<F, E>( &self, scope: &mut v8::PinScope, f: F, ) -> Result<v8::Global<v8::Object>, E> where F: FnOnce(&mut v8::PinScope) -> Result<T, E>, { if let Some(obj) = self.cell.get() { return Ok(obj.clone()); } let v = f(scope)?; let obj = make_cppgc_object(scope, v); let global = v8::Global::new(scope, obj); // `set` returns Err if a re-entrant call beat us to it; in that case // we discard our freshly-built object and fall through to the now- // populated cache. Either way `cell.get()` returns Some afterwards. let _ = self.cell.set(global); Ok(self.cell.get().unwrap().clone()) } pub fn set( &self, scope: &mut v8::PinScope, value: T, ) -> Result<(), v8::Global<v8::Object>> { let obj = make_cppgc_object(scope, value); self.cell.set(v8::Global::new(scope, obj)) } pub fn try_unwrap(&self, scope: &mut v8::PinScope) -> Option<UnsafePtr<T>> { let obj = self.cell.get()?; let val = v8::Local::new(scope, obj); try_unwrap_cppgc_object(scope, val.cast()) } } /// Indicates that `Self` is a CppGC type that structurally inherits from `T`. /// /// When implemented for a type, it declares that `Self` contains `T` as its /// first field (at offset 0) in a `#[repr(C)]` layout. This enables the runtime /// to safely reinterpret a pointer to the derived type as a pointer to the base /// type, allowing base-class methods to operate on derived instances. /// /// This trait is transitive: if `C: Inherits<B>` and `B: Inherits<A>`, then /// `C: Inherits<A>` is also implemented automatically by the derive macro. /// /// # Safety /// /// The implementor must guarantee that: /// /// 1. `Self` is `#[repr(C)]`. /// 2. The first field of `Self` is of type `T` and is at offset 0. /// 3. `T` is a valid CppGC type (implements [`GarbageCollected`]). /// /// These invariants ensure that a `CppGcObject<Self>` pointer can be safely /// cast to `CppGcObject<T>`, because the `tag` field and the base type's /// fields are at the same memory offsets in both layouts. /// /// Use `#[derive(CppgcInherits)]` with `#[cppgc_inherits_from(BaseType)]` /// instead of implementing this trait manually. The derive macro validates /// the layout requirements at compile time. pub unsafe trait Inherits<T: GarbageCollected + 'static>: GarbageCollected + 'static { } // Build up a graph of inheritance relationships and find all the transitive inheritors // using the inventory of InheritanceEdge structs. // This lets us know which Types are inheritors of a given Type. // This may be a bit expensive due to the hashing (though the input sizes are small), but it's only done once per Type. After that // it's cached. fn find_transitive_inheritors(root: TypeId) -> Vec<TypeId> { let mut adjacency_map: HashMap<TypeId, Vec<TypeId>> = HashMap::new(); for edge in inventory::iter::<InheritanceEdge> { adjacency_map .entry(edge.base) .or_default() .push(edge.derived); } let mut descendants = Vec::new(); let mut queue = vec![root]; let mut visited = std::collections::HashSet::new(); visited.insert(root); while let Some(current) = queue.pop() { if let Some(children) = adjacency_map.get(¤t) { for &child in children { if visited.insert(child) { descendants.push(child); queue.push(child); } } } } descendants } /// Marks a CppGC type as a base class that other types may inherit from. /// /// Types implementing `Base` can be used with [`try_unwrap_cppgc_base_object`], /// which accepts both the base type itself and any type that implements /// [`Inherits<Self>`](Inherits). This is what powers polymorphic method dispatch /// on CppGC objects: methods defined on a base class can be called on instances /// of any derived class. /// /// The trait provides [`inheriting_types`](Base::inheriting_types), which returns /// the [`TypeId`]s of all types that transitively inherit from `Self`. This list /// is computed once (lazily) using the inheritance graph built by the /// `CppgcInherits` derive macro and cached for the lifetime of the program. /// /// # Safety /// /// The implementor must guarantee that: /// /// 1. `Self` is `#[repr(C)]` and non-zero-sized. /// 2. Any type `D` for which `D: Inherits<Self>` holds has `Self` embedded at /// offset 0, so that a `CppGcObject<D>` can be safely reinterpreted as /// `CppGcObject<Self>`. /// /// Use `#[derive(CppgcBase)]` instead of implementing this trait manually. pub unsafe trait Base: GarbageCollected + 'static { #[doc(hidden)] fn __cache() -> &'static std::sync::OnceLock<Vec<TypeId>>; /// Returns the [`TypeId`]s of all types that transitively inherit from this type. fn inheriting_types() -> &'static [TypeId] { Self::__cache() .get_or_init(|| find_transitive_inheritors(TypeId::of::<Self>())) } } pub const fn verify_inherits< Base: GarbageCollected + 'static, Derived: Inherits<Base>, >() -> InheritanceEdge { InheritanceEdge { base: TypeId::of::<Base>(), derived: TypeId::of::<Derived>(), _private: Private, } } struct Private; pub struct InheritanceEdge { pub base: TypeId, pub derived: TypeId, _private: Private, // make sure it can't be constructed outside of this module } inventory::collect!(InheritanceEdge); #[cfg(test)] mod tests { use std::any::TypeId; use deno_ops::CppgcBase; use deno_ops::CppgcInherits; use super::*; #[repr(C)] #[derive(CppgcBase)] struct BaseType { _value: u8, } unsafe impl GarbageCollected for BaseType { fn trace(&self, _: &mut v8::cppgc::Visitor) {} fn get_name(&self) -> &'static std::ffi::CStr { c"BaseType" } } #[repr(C)] #[derive(CppgcInherits, CppgcBase)] #[cppgc_inherits_from(BaseType)] struct Derived { base: BaseType, _extra: u8, } unsafe impl GarbageCollected for Derived { fn trace(&self, _: &mut v8::cppgc::Visitor) {} fn get_name(&self) -> &'static std::ffi::CStr { c"DerivedType" } } const fn check<A: Inherits<B>, B: Base>() {} const _: () = { check::<Derived, BaseType>(); check::<Derived2, BaseType>(); check::<Derived2, Derived>(); }; #[test] fn inheriting_types_list_contains_derived() { assert!(BaseType::inheriting_types().contains(&TypeId::of::<Derived>())); assert!(BaseType::inheriting_types().contains(&TypeId::of::<Derived2>())); assert!(Derived::inheriting_types().contains(&TypeId::of::<Derived2>())); } unsafe impl GarbageCollected for Derived2 { fn trace(&self, _: &mut v8::cppgc::Visitor) {} fn get_name(&self) -> &'static std::ffi::CStr { c"Derived2" } } #[repr(C)] #[derive(CppgcInherits)] #[cppgc_inherits_from(Derived)] struct Derived2 { base: Derived, _value: u8, } }