/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libs/core/external.rs
275 строк
9 KB
Bartek Iwańczuk
chore: upgrade Rust toolchain to 1.95.0 (#33294)
07 май 2026, 09:44
Не верифицирован
07 май 2026, 09:44
269f316
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::marker::PhantomData; use std::mem::ManuallyDrop; /// Define an external type. #[macro_export] macro_rules! external { ($type:ty, $name:literal) => { impl $crate::Externalizable for $type { fn external_marker() -> ::core::primitive::usize { // Use the address of a static mut as a way to get around lack of usize-sized TypeId. Because it is mutable, the // compiler cannot collapse multiple definitions into one. static mut DEFINITION: $crate::ExternalDefinition = $crate::ExternalDefinition::new($name); // SAFETY: Wash the pointer through black_box so the compiler cannot see what we're going to do with it and needs // to assume it will be used for valid purposes. We are taking the address of a static item, but we avoid taking an // intermediate mutable reference to make this safe. let ptr = ::std::hint::black_box(::std::ptr::addr_of_mut!(DEFINITION)); ptr as ::core::primitive::usize } fn external_name() -> &'static ::core::primitive::str { $name } } }; } pub trait Externalizable { fn external_marker() -> usize; fn external_name() -> &'static str; } #[doc(hidden)] pub struct ExternalDefinition { #[allow(unused, reason = "used for documentation and debugging")] pub name: &'static str, } impl ExternalDefinition { #[doc(hidden)] pub const fn new(name: &'static str) -> Self { Self { name } } } #[repr(C)] struct ExternalWithMarker<T> { marker: usize, external: T, } /// A strongly-typed external pointer. As this is a shared pointer, it only provides immutable references to /// the underlying data. To allow for interior mutation, use an interior-mutable container such as [`RefCell`]. #[repr(transparent)] pub struct ExternalPointer<E: Externalizable> { ptr: *mut ManuallyDrop<ExternalWithMarker<E>>, _type: std::marker::PhantomData<E>, } impl<E: Externalizable> ExternalPointer<E> { pub fn new(external: E) -> Self { let marker = E::external_marker(); let new = Box::new(ManuallyDrop::new(ExternalWithMarker { marker, external })); ExternalPointer { ptr: Box::into_raw(new), _type: PhantomData, } } pub fn into_raw(self) -> *const std::ffi::c_void { self.ptr as _ } /// Create an [`ExternalPointer`] from a raw pointer. This does not validate the pointer at all. pub fn from_raw(ptr: *const std::ffi::c_void) -> Self { ExternalPointer { ptr: ptr as _, _type: PhantomData, } } /// Checks the alignment and marker of the pointer's data. If this is not a valid pointer for any reason, /// panics. If there is a mismatch here there is a serious programming error somewhere in either Rust or JavaScript /// and we cannot risk continuing. fn validate_pointer(&self) -> *mut ExternalWithMarker<E> { let expected_marker = E::external_marker(); // SAFETY: we assume the pointer is valid. If it is not, we risk a crash but that's // unfortunately not something we can easily test. if self.ptr.is_null() || self.ptr.align_offset(std::mem::align_of::<usize>()) != 0 || unsafe { std::ptr::read::<usize>(self.ptr as _) } != expected_marker { panic!( "Detected an invalid v8::External (expected {})", E::external_name() ); } self.ptr as _ } /// Unsafely retrieves the underlying object from this pointer after validating it. /// /// # Safety /// /// This method is inherently unsafe because we cannot know if the underlying memory has been deallocated at some point. /// /// The lifetime of the return value is tied to the pointer itself, however you must take care not to use methods that /// mutate the underlying pointer such as `unsafely_take` while this reference is alive. pub unsafe fn unsafely_deref(&self) -> &E { unsafe { let validated_ptr = self.validate_pointer(); let external = std::ptr::addr_of!((*validated_ptr).external); &*external } } /// Unsafely takes the object from this external. /// /// # Safety /// /// This method is inherently unsafe because we cannot know if /// the underlying memory has been deallocated at some point. /// /// You must ensure that no other references to this object are alive at the time you call this method. pub unsafe fn unsafely_take(self) -> E { unsafe { let validated_ptr = self.validate_pointer(); let marker = std::ptr::addr_of_mut!((*validated_ptr).marker); // Ensure that this object has not been taken assert_ne!(std::ptr::replace(marker, 0), 0); std::ptr::write(marker, 0); let external = std::ptr::read(std::ptr::addr_of!((*validated_ptr).external)); // Deallocate without dropping _ = Box::<ManuallyDrop<ExternalWithMarker<E>>>::from_raw(self.ptr); external } } } #[cfg(test)] mod tests { use super::*; struct External1(u32); external!(External1, "external 1"); struct External2(()); external!(External2, "external 2"); // Use the same name as External 1 struct External1b(()); external!(External1b, "external 1"); /// Use this to avoid leaking in miri tests struct DeallocOnPanic<E: Externalizable>(Option<ExternalPointer<E>>); impl<E: Externalizable> DeallocOnPanic<E> { pub fn new(external: &ExternalPointer<E>) -> Self { Self(Some(ExternalPointer { ptr: external.ptr, _type: PhantomData, })) } } impl<E: Externalizable> Drop for DeallocOnPanic<E> { fn drop(&mut self) { unsafe { self.0.take().unwrap().unsafely_take(); } } } #[test] pub fn test_external() { let external = ExternalPointer::new(External1(1)); assert_eq!(unsafe { external.unsafely_deref() }.0, 1); let ptr = external.into_raw(); let external = ExternalPointer::<External1>::from_raw(ptr); assert_eq!(unsafe { external.unsafely_deref() }.0, 1); assert_eq!(unsafe { external.unsafely_take() }.0, 1); } // If this test ever fails then our "pseudo type ID" system is not working as expected. Each of these are considered // different "types" of externals and must have different markers. #[test] pub fn test_external_markers() { let m1 = External1::external_marker(); let m2 = External2::external_marker(); let m1b = External1b::external_marker(); assert_ne!(m1, m2); assert_ne!(m1, m1b); } // If this test ever fails then our "pseudo type ID" system is not working as expected. Each of these are considered // different "types" of externals and must have different markers, and we must not be able to deref across these // different external types. #[test] #[should_panic] pub fn test_external_incompatible_same_name() { let external = ExternalPointer::new(External1(1)); let _dealloc = DeallocOnPanic::new(&external); assert_eq!(unsafe { external.unsafely_deref() }.0, 1); let ptr = external.into_raw(); let external = ExternalPointer::<External1b>::from_raw(ptr); unsafe { external.unsafely_deref(); } } #[test] #[should_panic] pub fn test_external_deref_after_take() { let external = ExternalPointer::new(External1(1)); // Raw dealloc guard that frees the allocation on panic without going // through validate_pointer (which would also panic on the zeroed marker). struct RawDealloc(*mut ManuallyDrop<ExternalWithMarker<External1>>); impl Drop for RawDealloc { fn drop(&mut self) { unsafe { drop(Box::from_raw(self.0)); } } } let _guard = RawDealloc(external.ptr); // Zero the marker to simulate what unsafely_take does, but without // deallocating. The previous version of this test called unsafely_take // (which frees memory) then unsafely_deref on the freed pointer, which // is UB and broke under newer LLVM. unsafe { std::ptr::write(external.ptr as *mut usize, 0); } // Should panic: marker is 0 which won't match the expected marker unsafe { external.unsafely_deref(); } } #[test] #[should_panic] pub fn test_external_incompatible_deref() { let external = ExternalPointer::new(External1(1)); let _dealloc = DeallocOnPanic::new(&external); assert_eq!(unsafe { external.unsafely_deref() }.0, 1); let ptr = external.into_raw(); let external = ExternalPointer::<External2>::from_raw(ptr); unsafe { external.unsafely_deref(); } } #[test] #[should_panic] pub fn test_external_incompatible_take() { let external = ExternalPointer::new(External1(1)); let _dealloc = DeallocOnPanic::new(&external); assert_eq!(unsafe { external.unsafely_deref() }.0, 1); let ptr = external.into_raw(); let external = ExternalPointer::<External2>::from_raw(ptr); unsafe { external.unsafely_take(); } } }