use std::any::TypeId;
struct CppGcObject<T> {
tag: TypeId,
member: T,
}
impl<T> v8::cppgc::GarbageCollected for CppGcObject<T> {
fn trace(&self, _: &v8::cppgc::Visitor) {}
}
pub(crate) const DEFAULT_CPP_GC_EMBEDDER_ID: u16 = 0xde90;
const EMBEDDER_ID_OFFSET: i32 = 0;
const SLOT_OFFSET: i32 = 1;
const FIELD_COUNT: usize = 2;
pub fn make_cppgc_object<'a, T: 'static>(
scope: &mut v8::HandleScope<'a>,
t: T,
) -> v8::Local<'a, v8::Object> {
let templ = v8::ObjectTemplate::new(scope);
templ.set_internal_field_count(FIELD_COUNT);
let obj = templ.new_instance(scope).unwrap();
let member = v8::cppgc::make_garbage_collected(
scope.get_cpp_heap().unwrap(),
Box::new(CppGcObject {
tag: TypeId::of::<T>(),
member: t,
}),
);
obj.set_aligned_pointer_in_internal_field(
EMBEDDER_ID_OFFSET,
&DEFAULT_CPP_GC_EMBEDDER_ID as *const u16 as _,
);
obj.set_aligned_pointer_in_internal_field(SLOT_OFFSET, member.handle as _);
obj
}
pub fn try_unwrap_cppgc_object<'sc, T: 'static>(
val: v8::Local<v8::Value>,
) -> Option<&'sc T> {
let Ok(obj): Result<v8::Local<v8::Object>, _> = val.try_into() else {
return None;
};
if obj.internal_field_count() != FIELD_COUNT {
return None;
}
let member = unsafe {
let ptr = obj.get_aligned_pointer_from_internal_field(SLOT_OFFSET);
let obj = &*(ptr as *const v8::cppgc::InnerMember);
obj.get::<CppGcObject<T>>()
};
if member.tag != TypeId::of::<T>() {
return None;
}
Some(&member.member)
}