Add AsHandleValue trait to Heap<Value> and make Heap values rooted (#38024)

Encapsulates the unsafe conversion from Heap<Value> to HandleValue<'a>,
and reducing repetitive unsafe code at call.

fix #37258
This commit is contained in:
Taym Haddadi 2025-08-04 18:42:53 +02:00 committed by GitHub
parent 9416251cab
commit 04ec710e60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 204 additions and 146 deletions

4
Cargo.lock generated
View file

@ -5240,7 +5240,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs" name = "mozjs"
version = "0.14.1" version = "0.14.1"
source = "git+https://github.com/servo/mozjs#4035b0c4e9e2df5cacc68c4b71e7375a48605902" source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4"
dependencies = [ dependencies = [
"bindgen 0.71.1", "bindgen 0.71.1",
"cc", "cc",
@ -5252,7 +5252,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs_sys" name = "mozjs_sys"
version = "0.128.13-3" version = "0.128.13-3"
source = "git+https://github.com/servo/mozjs#4035b0c4e9e2df5cacc68c4b71e7375a48605902" source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4"
dependencies = [ dependencies = [
"bindgen 0.71.1", "bindgen 0.71.1",
"cc", "cc",

View file

@ -42,6 +42,7 @@ use js::typedarray::{
}; };
use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::trace::RootedTraceableBox;
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::{CanGc, JSContext}; use crate::script_runtime::{CanGc, JSContext};
@ -52,15 +53,14 @@ use crate::script_runtime::{CanGc, JSContext};
/// provides a view onto an `ArrayBuffer`. /// provides a view onto an `ArrayBuffer`.
/// ///
/// See: <https://webidl.spec.whatwg.org/#BufferSource> /// See: <https://webidl.spec.whatwg.org/#BufferSource>
#[derive(PartialEq)]
pub(crate) enum BufferSource { pub(crate) enum BufferSource {
/// Represents an `ArrayBufferView` (e.g., `Uint8Array`, `DataView`). /// Represents an `ArrayBufferView` (e.g., `Uint8Array`, `DataView`).
/// See: <https://webidl.spec.whatwg.org/#ArrayBufferView> /// See: <https://webidl.spec.whatwg.org/#ArrayBufferView>
ArrayBufferView(Box<Heap<*mut JSObject>>), ArrayBufferView(RootedTraceableBox<Heap<*mut JSObject>>),
/// Represents an `ArrayBuffer`, a fixed-length binary data buffer. /// Represents an `ArrayBuffer`, a fixed-length binary data buffer.
/// See: <https://webidl.spec.whatwg.org/#idl-ArrayBuffer> /// See: <https://webidl.spec.whatwg.org/#idl-ArrayBuffer>
ArrayBuffer(Box<Heap<*mut JSObject>>), ArrayBuffer(RootedTraceableBox<Heap<*mut JSObject>>),
} }
pub(crate) fn create_heap_buffer_source_with_length<T>( pub(crate) fn create_heap_buffer_source_with_length<T>(
@ -80,7 +80,7 @@ where
} }
Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView( Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
Heap::boxed(*array.handle()), RootedTraceableBox::from_box(Heap::boxed(*array.handle())),
))) )))
} }
@ -100,8 +100,8 @@ where
BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => match &other
.buffer_source .buffer_source
{ {
BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => unsafe { BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
heap.handle() == from_heap.handle() std::ptr::eq(heap.get(), from_heap.get())
}, },
}, },
} }
@ -122,14 +122,16 @@ where
pub(crate) fn from_view( pub(crate) fn from_view(
chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>, chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
) -> HeapBufferSource<T> { ) -> HeapBufferSource<T> {
HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(unsafe { HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(RootedTraceableBox::from_box(
*chunk.underlying_object() Heap::boxed(unsafe { *chunk.underlying_object() }),
}))) )))
} }
pub(crate) fn default() -> Self { pub(crate) fn default() -> Self {
HeapBufferSource { HeapBufferSource {
buffer_source: BufferSource::ArrayBufferView(Heap::boxed(std::ptr::null_mut())), buffer_source: BufferSource::ArrayBufferView(RootedTraceableBox::from_box(
Heap::boxed(std::ptr::null_mut()),
)),
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -174,11 +176,11 @@ where
BufferSource::ArrayBufferView(buffer) => unsafe { BufferSource::ArrayBufferView(buffer) => unsafe {
let mut is_shared = false; let mut is_shared = false;
rooted!(in (*cx) let view_buffer = rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared)); JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(Heap::boxed( HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(
*view_buffer.handle(), RootedTraceableBox::from_box(Heap::boxed(*view_buffer.handle())),
))) ))
}, },
BufferSource::ArrayBuffer(_) => { BufferSource::ArrayBuffer(_) => {
unreachable!("BufferSource::ArrayBuffer does not have a view buffer.") unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
@ -196,7 +198,7 @@ where
// assert buffer is an ArrayBuffer view // assert buffer is an ArrayBuffer view
assert!(JS_IsArrayBufferViewObject(*buffer.handle())); assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
rooted!(in (*cx) let view_buffer = rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared)); JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
// This buffer is always created unshared // This buffer is always created unshared
debug_assert!(!is_shared); debug_assert!(!is_shared);
// Detach the ArrayBuffer // Detach the ArrayBuffer
@ -204,7 +206,7 @@ where
} }
}, },
BufferSource::ArrayBuffer(buffer) => unsafe { BufferSource::ArrayBuffer(buffer) => unsafe {
DetachArrayBuffer(*cx, Handle::from_raw(buffer.handle())) DetachArrayBuffer(*cx, Handle::from_raw(buffer.handle().into()))
}, },
} }
} }
@ -226,7 +228,7 @@ where
unsafe { unsafe {
assert!(JS_IsArrayBufferViewObject(*buffer.handle())); assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
rooted!(in (*cx) let view_buffer = rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared)); JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
debug_assert!(!is_shared); debug_assert!(!is_shared);
IsDetachedArrayBufferObject(*view_buffer.handle()) IsDetachedArrayBufferObject(*view_buffer.handle())
} }
@ -245,7 +247,7 @@ where
unsafe { unsafe {
assert!(JS_IsArrayBufferViewObject(*buffer.handle())); assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
rooted!(in (*cx) let view_buffer = rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, buffer.handle(), &mut is_shared)); JS_GetArrayBufferViewBuffer(*cx, buffer.handle().into(), &mut is_shared));
debug_assert!(!is_shared); debug_assert!(!is_shared);
GetArrayBufferByteLength(*view_buffer.handle()) GetArrayBufferByteLength(*view_buffer.handle())
} }
@ -429,13 +431,16 @@ where
) -> Option<HeapBufferSource<ArrayBufferU8>> { ) -> Option<HeapBufferSource<ArrayBufferU8>> {
match &self.buffer_source { match &self.buffer_source {
BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => { BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
let result = let result = unsafe {
unsafe { ArrayBufferClone(*cx, heap.handle(), byte_offset, byte_length) }; ArrayBufferClone(*cx, heap.handle().into(), byte_offset, byte_length)
};
if result.is_null() { if result.is_null() {
None None
} else { } else {
Some(HeapBufferSource::<ArrayBufferU8>::new( Some(HeapBufferSource::<ArrayBufferU8>::new(
BufferSource::ArrayBuffer(Heap::boxed(result)), BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(
result,
))),
)) ))
} }
}, },
@ -466,11 +471,9 @@ where
match &from_buffer.buffer_source { match &from_buffer.buffer_source {
BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBufferView(from_heap) |
BufferSource::ArrayBuffer(from_heap) => { BufferSource::ArrayBuffer(from_heap) => {
unsafe { if std::ptr::eq(heap.get(), from_heap.get()) {
if heap.handle() == from_heap.handle() { return false;
return false; }
}
};
}, },
} }
}, },
@ -514,9 +517,9 @@ where
BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBufferView(from_heap) |
BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData( BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
*cx, *cx,
heap.handle(), heap.handle().into(),
dest_start, dest_start,
from_heap.handle(), from_heap.handle().into(),
from_byte_offset, from_byte_offset,
bytes_to_copy, bytes_to_copy,
), ),
@ -541,7 +544,7 @@ where
let mut is_defined = false; let mut is_defined = false;
match &self.buffer_source { match &self.buffer_source {
BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe { BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => unsafe {
if !HasDefinedArrayBufferDetachKey(*cx, heap.handle(), &mut is_defined) { if !HasDefinedArrayBufferDetachKey(*cx, heap.handle().into(), &mut is_defined) {
return false; return false;
} }
}, },
@ -568,7 +571,7 @@ where
// Step 2 (Reordered) // Step 2 (Reordered)
let buffer_data = match &self.buffer_source { let buffer_data = match &self.buffer_source {
BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe { BufferSource::ArrayBufferView(buffer) | BufferSource::ArrayBuffer(buffer) => unsafe {
StealArrayBufferContents(*cx, buffer.handle()) StealArrayBufferContents(*cx, buffer.handle().into())
}, },
}; };
@ -588,9 +591,9 @@ where
// whose [[ArrayBufferData]] internal slot value is arrayBufferData and // whose [[ArrayBufferData]] internal slot value is arrayBufferData and
// whose [[ArrayBufferByteLength]] internal slot value is arrayBufferByteLength. // whose [[ArrayBufferByteLength]] internal slot value is arrayBufferByteLength.
Ok(HeapBufferSource::<ArrayBufferU8>::new( Ok(HeapBufferSource::<ArrayBufferU8>::new(
BufferSource::ArrayBuffer(Heap::boxed(unsafe { BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(unsafe {
NewArrayBufferWithContents(*cx, buffer_length, buffer_data) NewArrayBufferWithContents(*cx, buffer_length, buffer_data)
})), }))),
)) ))
} }
} }
@ -679,9 +682,9 @@ pub(crate) fn create_buffer_source_with_constructor(
match &buffer_source.buffer_source { match &buffer_source.buffer_source {
BufferSource::ArrayBuffer(heap) => match constructor { BufferSource::ArrayBuffer(heap) => match constructor {
Constructor::DataView => Ok(HeapBufferSource::new(BufferSource::ArrayBufferView( Constructor::DataView => Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
Heap::boxed(unsafe { RootedTraceableBox::from_box(Heap::boxed(unsafe {
JS_NewDataView(*cx, heap.handle(), byte_offset, byte_length) JS_NewDataView(*cx, heap.handle().into(), byte_offset, byte_length)
}), })),
))), ))),
Constructor::Name(name_type) => construct_typed_array( Constructor::Name(name_type) => construct_typed_array(
cx, cx,
@ -709,45 +712,78 @@ fn construct_typed_array(
BufferSource::ArrayBuffer(heap) => { BufferSource::ArrayBuffer(heap) => {
let array_view = unsafe { let array_view = unsafe {
match name_type { match name_type {
Type::Int8 => { Type::Int8 => JS_NewInt8ArrayWithBuffer(
JS_NewInt8ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Uint8 => {
JS_NewUint8ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Uint16 => {
JS_NewUint16ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Int16 => {
JS_NewInt16ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Int32 => {
JS_NewInt32ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Uint32 => {
JS_NewUint32ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Float32 => {
JS_NewFloat32ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Float64 => {
JS_NewFloat64ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
*cx, *cx,
heap.handle(), heap.handle().into(),
byte_offset,
byte_length,
),
Type::Uint8 => JS_NewUint8ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Uint16 => JS_NewUint16ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Int16 => JS_NewInt16ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Int32 => JS_NewInt32ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Uint32 => JS_NewUint32ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Float32 => JS_NewFloat32ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Float64 => JS_NewFloat64ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Uint8Clamped => JS_NewUint8ClampedArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::BigInt64 => JS_NewBigInt64ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::BigUint64 => JS_NewBigUint64ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset,
byte_length,
),
Type::Float16 => JS_NewFloat16ArrayWithBuffer(
*cx,
heap.handle().into(),
byte_offset, byte_offset,
byte_length, byte_length,
), ),
Type::BigInt64 => {
JS_NewBigInt64ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::BigUint64 => {
JS_NewBigUint64ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Float16 => {
JS_NewFloat16ArrayWithBuffer(*cx, heap.handle(), byte_offset, byte_length)
},
Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => { Type::Int64 | Type::Simd128 | Type::MaxTypedArrayViewType => {
unreachable!("Invalid TypedArray type") unreachable!("Invalid TypedArray type")
}, },
@ -755,7 +791,7 @@ fn construct_typed_array(
}; };
Ok(HeapBufferSource::new(BufferSource::ArrayBufferView( Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
Heap::boxed(array_view), RootedTraceableBox::from_box(Heap::boxed(array_view)),
))) )))
}, },
BufferSource::ArrayBufferView(_) => { BufferSource::ArrayBufferView(_) => {
@ -779,7 +815,7 @@ pub(crate) fn create_array_buffer_with_size(
Err(Error::Type("can't create array buffer".to_owned())) Err(Error::Type("can't create array buffer".to_owned()))
} else { } else {
Ok(HeapBufferSource::<ArrayBufferU8>::new( Ok(HeapBufferSource::<ArrayBufferU8>::new(
BufferSource::ArrayBuffer(Heap::boxed(result)), BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(result))),
)) ))
} }
} }
@ -866,6 +902,7 @@ impl DataBlock {
#[cfg(feature = "webgpu")] #[cfg(feature = "webgpu")]
#[derive(JSTraceable, MallocSizeOf)] #[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) struct DataView { pub(crate) struct DataView {
#[no_trace] #[no_trace]
range: Range<usize>, range: Range<usize>,

View file

@ -12,6 +12,7 @@ use crate::dom::bindings::utils::to_frozen_array;
use crate::script_runtime::{CanGc, JSContext}; use crate::script_runtime::{CanGc, JSContext};
#[derive(JSTraceable)] #[derive(JSTraceable)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub(crate) struct CachedFrozenArray { pub(crate) struct CachedFrozenArray {
frozen_value: DomRefCell<Option<Heap<JSVal>>>, frozen_value: DomRefCell<Option<Heap<JSVal>>>,
} }

View file

@ -30,7 +30,8 @@ use std::hash::{Hash, Hasher};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::{mem, ptr}; use std::{mem, ptr};
use js::jsapi::{JSObject, JSTracer}; use js::jsapi::{Heap, JSObject, JSTracer, Value};
use js::rust::HandleValue;
use layout_api::TrustedNodeAddress; use layout_api::TrustedNodeAddress;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
pub(crate) use script_bindings::root::*; pub(crate) use script_bindings::root::*;
@ -409,3 +410,21 @@ where
&*(slice as *const [Dom<T>] as *const [LayoutDom<T>]) &*(slice as *const [Dom<T>] as *const [LayoutDom<T>])
} }
} }
/// Converts a rooted `Heap<Value>` into a `HandleValue`.
///
/// This is only safe if the `Heap` is rooted (e.g., held inside a `Dom`-managed struct),
/// and the `#[must_root]` crown lint is active to enforce rooting at compile time.
/// Avoids repeating unsafe `from_raw` calls at each usage site.
pub trait AsHandleValue<'a> {
fn as_handle_value(&'a self) -> HandleValue<'a>;
}
impl<'a> AsHandleValue<'a> for Heap<Value> {
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
fn as_handle_value(&'a self) -> HandleValue<'a> {
// SAFETY: `self` is assumed to be rooted, and `handle()` ties
// the lifetime to `&self`, which the compiler can enforce.
unsafe { HandleValue::from_marked_location(self.ptr.get() as *const _) }
}
}

View file

@ -14,7 +14,7 @@ use js::glue::UnwrapObjectStatic;
use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject}; use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject};
use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue}; use js::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue};
use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue}; use js::rust::wrappers::{Construct1, JS_GetProperty, SameValue};
use js::rust::{HandleObject, HandleValue, MutableHandleValue}; use js::rust::{HandleObject, MutableHandleValue};
use script_bindings::conversions::{SafeFromJSValConvertible, SafeToJSValConvertible}; use script_bindings::conversions::{SafeFromJSValConvertible, SafeToJSValConvertible};
use super::bindings::trace::HashMapTracedValues; use super::bindings::trace::HashMapTracedValues;
@ -32,7 +32,7 @@ use crate::dom::bindings::error::{
}; };
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{AsHandleValue, Dom, DomRoot};
use crate::dom::bindings::settings_stack::is_execution_stack_empty; use crate::dom::bindings::settings_stack::is_execution_stack_empty;
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document; use crate::dom::document::Document;
@ -999,7 +999,6 @@ pub(crate) enum CustomElementReaction {
impl CustomElementReaction { impl CustomElementReaction {
/// <https://html.spec.whatwg.org/multipage/#invoke-custom-element-reactions> /// <https://html.spec.whatwg.org/multipage/#invoke-custom-element-reactions>
#[allow(unsafe_code)]
pub(crate) fn invoke(&self, element: &Element, can_gc: CanGc) { pub(crate) fn invoke(&self, element: &Element, can_gc: CanGc) {
// Step 2.1 // Step 2.1
match *self { match *self {
@ -1008,10 +1007,7 @@ impl CustomElementReaction {
}, },
CustomElementReaction::Callback(ref callback, ref arguments) => { CustomElementReaction::Callback(ref callback, ref arguments) => {
// We're rooted, so it's safe to hand out a handle to objects in Heap // We're rooted, so it's safe to hand out a handle to objects in Heap
let arguments = arguments let arguments = arguments.iter().map(|arg| arg.as_handle_value()).collect();
.iter()
.map(|arg| unsafe { HandleValue::from_raw(arg.handle()) })
.collect();
rooted!(in(*GlobalScope::get_cx()) let mut value: JSVal); rooted!(in(*GlobalScope::get_cx()) let mut value: JSVal);
let _ = callback.Call_( let _ = callback.Call_(
element, element,
@ -1112,6 +1108,7 @@ impl CustomElementReactionStack {
} }
/// <https://html.spec.whatwg.org/multipage/#enqueue-a-custom-element-callback-reaction> /// <https://html.spec.whatwg.org/multipage/#enqueue-a-custom-element-callback-reaction>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn enqueue_callback_reaction( pub(crate) fn enqueue_callback_reaction(
&self, &self,
element: &Element, element: &Element,

View file

@ -422,6 +422,7 @@ impl Element {
self.ensure_rare_data().custom_element_definition = None; self.ensure_rare_data().custom_element_definition = None;
} }
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) { pub(crate) fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
self.ensure_rare_data() self.ensure_rare_data()
.custom_element_reaction_queue .custom_element_reaction_queue

View file

@ -115,7 +115,7 @@ pub(crate) enum FileReaderReadyState {
#[derive(JSTraceable, MallocSizeOf)] #[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum FileReaderResult { pub(crate) enum FileReaderResult {
ArrayBuffer(#[ignore_malloc_size_of = "mozjs"] Heap<JSVal>), ArrayBuffer(#[ignore_malloc_size_of = "mozjs"] RootedTraceableBox<Heap<JSVal>>),
String(DOMString), String(DOMString),
} }
@ -354,7 +354,8 @@ impl FileReader {
.is_ok() .is_ok()
); );
*result.borrow_mut() = Some(FileReaderResult::ArrayBuffer(Heap::default())); *result.borrow_mut() =
Some(FileReaderResult::ArrayBuffer(RootedTraceableBox::default()));
if let Some(FileReaderResult::ArrayBuffer(ref mut heap)) = *result.borrow_mut() { if let Some(FileReaderResult::ArrayBuffer(ref mut heap)) = *result.borrow_mut() {
heap.set(jsval::ObjectValue(array_buffer.get())); heap.set(jsval::ObjectValue(array_buffer.get()));

View file

@ -24,7 +24,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{AsHandleValue, Dom, DomRoot};
use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::bindings::structuredclone; use crate::dom::bindings::structuredclone;
use crate::dom::event::Event; use crate::dom::event::Event;
@ -53,18 +53,18 @@ pub(crate) struct History {
impl History { impl History {
pub(crate) fn new_inherited(window: &Window) -> History { pub(crate) fn new_inherited(window: &Window) -> History {
let state = Heap::default();
state.set(NullValue());
History { History {
reflector_: Reflector::new(), reflector_: Reflector::new(),
window: Dom::from_ref(window), window: Dom::from_ref(window),
state, state: Heap::default(),
state_id: Cell::new(None), state_id: Cell::new(None),
} }
} }
pub(crate) fn new(window: &Window, can_gc: CanGc) -> DomRoot<History> { pub(crate) fn new(window: &Window, can_gc: CanGc) -> DomRoot<History> {
reflect_dom_object(Box::new(History::new_inherited(window)), window, can_gc) let dom_root = reflect_dom_object(Box::new(History::new_inherited(window)), window, can_gc);
dom_root.state.set(NullValue());
dom_root
} }
} }
@ -84,7 +84,6 @@ impl History {
/// <https://html.spec.whatwg.org/multipage/#history-traversal> /// <https://html.spec.whatwg.org/multipage/#history-traversal>
/// Steps 5-16 /// Steps 5-16
#[allow(unsafe_code)]
pub(crate) fn activate_state( pub(crate) fn activate_state(
&self, &self,
state_id: Option<HistoryStateId>, state_id: Option<HistoryStateId>,
@ -145,7 +144,7 @@ impl History {
PopStateEvent::dispatch_jsval( PopStateEvent::dispatch_jsval(
self.window.upcast::<EventTarget>(), self.window.upcast::<EventTarget>(),
&self.window, &self.window,
unsafe { HandleValue::from_raw(self.state.handle()) }, self.state.as_handle_value(),
can_gc, can_gc,
); );
} }

View file

@ -166,7 +166,6 @@ impl Notification {
) -> Self { ) -> Self {
// TODO: missing call to https://html.spec.whatwg.org/multipage/#structuredserializeforstorage // TODO: missing call to https://html.spec.whatwg.org/multipage/#structuredserializeforstorage
// may be find in `dom/bindings/structuredclone.rs` // may be find in `dom/bindings/structuredclone.rs`
let data = Heap::default();
let title = title.clone(); let title = title.clone();
let dir = options.dir; let dir = options.dir;
@ -234,7 +233,7 @@ impl Notification {
serviceworker_registration: None, serviceworker_registration: None,
title, title,
body, body,
data, data: Heap::default(),
dir, dir,
image, image,
icon, icon,

View file

@ -35,6 +35,7 @@ use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::conversions::root_from_object; use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorToJsval}; use crate::dom::bindings::error::{Error, ErrorToJsval};
use crate::dom::bindings::reflector::{DomGlobal, DomObject, MutDomObject, Reflector}; use crate::dom::bindings::reflector::{DomGlobal, DomObject, MutDomObject, Reflector};
use crate::dom::bindings::root::AsHandleValue;
use crate::dom::bindings::settings_stack::AutoEntryScript; use crate::dom::bindings::settings_stack::AutoEntryScript;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler}; use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
@ -435,7 +436,6 @@ struct WaitForAllFulfillmentHandler {
} }
impl Callback for WaitForAllFulfillmentHandler { impl Callback for WaitForAllFulfillmentHandler {
#[allow(unsafe_code)]
fn callback(&self, _cx: SafeJSContext, v: HandleValue, _realm: InRealm, _can_gc: CanGc) { fn callback(&self, _cx: SafeJSContext, v: HandleValue, _realm: InRealm, _can_gc: CanGc) {
// Let fulfillmentHandler be the following steps given arg: // Let fulfillmentHandler be the following steps given arg:
@ -453,15 +453,10 @@ impl Callback for WaitForAllFulfillmentHandler {
// If fulfilledCount equals total, then perform successSteps given result. // If fulfilledCount equals total, then perform successSteps given result.
if equals_total { if equals_total {
// Safety: the values are kept alive by the Heap let result_ref = self.result.borrow();
// while their handles are passed to the the success steps. let result_handles: Vec<HandleValue> =
let result_handles: Vec<HandleValue> = unsafe { result_ref.iter().map(|v| v.as_handle_value()).collect();
self.result
.borrow()
.iter()
.map(|val| HandleValue::from_raw(val.handle()))
.collect()
};
(self.success_steps)(result_handles); (self.success_steps)(result_handles);
} }
} }
@ -496,6 +491,7 @@ impl Callback for WaitForAllRejectionHandler {
} }
/// <https://webidl.spec.whatwg.org/#wait-for-all> /// <https://webidl.spec.whatwg.org/#wait-for-all>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn wait_for_all( pub(crate) fn wait_for_all(
cx: SafeJSContext, cx: SafeJSContext,
global: &GlobalScope, global: &GlobalScope,

View file

@ -154,7 +154,7 @@ pub(crate) struct PipeTo {
/// The error potentially passed to shutdown, /// The error potentially passed to shutdown,
/// stored here because we must keep it across a microtask. /// stored here because we must keep it across a microtask.
#[ignore_malloc_size_of = "mozjs"] #[ignore_malloc_size_of = "mozjs"]
shutdown_error: Rc<RefCell<Option<Heap<JSVal>>>>, shutdown_error: Rc<RefCell<Option<RootedTraceableBox<Heap<JSVal>>>>>,
/// The promise returned by a shutdown action. /// The promise returned by a shutdown action.
/// We keep it to only continue when it is not pending anymore. /// We keep it to only continue when it is not pending anymore.
@ -376,7 +376,7 @@ impl PipeTo {
/// Setting shutdown error in a way that ensures it isn't /// Setting shutdown error in a way that ensures it isn't
/// moved after it has been set. /// moved after it has been set.
fn set_shutdown_error(&self, error: SafeHandleValue) { fn set_shutdown_error(&self, error: SafeHandleValue) {
*self.shutdown_error.borrow_mut() = Some(Heap::default()); *self.shutdown_error.borrow_mut() = Some(RootedTraceableBox::new(Heap::default()));
let Some(ref heap) = *self.shutdown_error.borrow() else { let Some(ref heap) = *self.shutdown_error.borrow() else {
unreachable!("Option set to Some(heap) above."); unreachable!("Option set to Some(heap) above.");
}; };

View file

@ -6,7 +6,7 @@ use std::rc::Rc;
use constellation_traits::ScriptToConstellationMessage; use constellation_traits::ScriptToConstellationMessage;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::Heap; use js::jsapi::HandleObject;
use webgpu_traits::WebGPUAdapterResponse; use webgpu_traits::WebGPUAdapterResponse;
use wgpu_types::PowerPreference; use wgpu_types::PowerPreference;
@ -117,7 +117,7 @@ impl RoutedPromiseListener<WebGPUAdapterResponse> for GPU {
"{} ({:?})", "{} ({:?})",
adapter.adapter_info.name, adapter.adapter_id.0 adapter.adapter_info.name, adapter.adapter_id.0
)), )),
Heap::default(), HandleObject::null(),
adapter.features, adapter.features,
adapter.limits, adapter.limits,
adapter.adapter_info, adapter.adapter_info,

View file

@ -5,7 +5,7 @@
use std::rc::Rc; use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject}; use js::jsapi::{HandleObject, Heap, JSObject};
use webgpu_traits::{ use webgpu_traits::{
RequestDeviceError, WebGPU, WebGPUAdapter, WebGPUDeviceResponse, WebGPURequest, RequestDeviceError, WebGPU, WebGPUAdapter, WebGPUDeviceResponse, WebGPURequest,
}; };
@ -49,7 +49,6 @@ impl GPUAdapter {
fn new_inherited( fn new_inherited(
channel: WebGPU, channel: WebGPU,
name: DOMString, name: DOMString,
extensions: Heap<*mut JSObject>,
features: &GPUSupportedFeatures, features: &GPUSupportedFeatures,
limits: &GPUSupportedLimits, limits: &GPUSupportedLimits,
info: &GPUAdapterInfo, info: &GPUAdapterInfo,
@ -59,7 +58,7 @@ impl GPUAdapter {
reflector_: Reflector::new(), reflector_: Reflector::new(),
channel, channel,
name, name,
extensions, extensions: Heap::default(),
features: Dom::from_ref(features), features: Dom::from_ref(features),
limits: Dom::from_ref(limits), limits: Dom::from_ref(limits),
info: Dom::from_ref(info), info: Dom::from_ref(info),
@ -72,7 +71,7 @@ impl GPUAdapter {
global: &GlobalScope, global: &GlobalScope,
channel: WebGPU, channel: WebGPU,
name: DOMString, name: DOMString,
extensions: Heap<*mut JSObject>, extensions: HandleObject,
features: wgpu_types::Features, features: wgpu_types::Features,
limits: wgpu_types::Limits, limits: wgpu_types::Limits,
info: wgpu_types::AdapterInfo, info: wgpu_types::AdapterInfo,
@ -82,13 +81,15 @@ impl GPUAdapter {
let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap(); let features = GPUSupportedFeatures::Constructor(global, None, features, can_gc).unwrap();
let limits = GPUSupportedLimits::new(global, limits, can_gc); let limits = GPUSupportedLimits::new(global, limits, can_gc);
let info = GPUAdapterInfo::new(global, info, can_gc); let info = GPUAdapterInfo::new(global, info, can_gc);
reflect_dom_object( let dom_root = reflect_dom_object(
Box::new(GPUAdapter::new_inherited( Box::new(GPUAdapter::new_inherited(
channel, name, extensions, &features, &limits, &info, adapter, channel, name, &features, &limits, &info, adapter,
)), )),
global, global,
can_gc, can_gc,
) );
dom_root.extensions.set(*extensions);
dom_root
} }
} }
@ -226,7 +227,7 @@ impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
&self.global(), &self.global(),
self.channel.clone(), self.channel.clone(),
self, self,
Heap::default(), HandleObject::null(),
descriptor.required_features, descriptor.required_features,
descriptor.required_limits, descriptor.required_limits,
device_id, device_id,
@ -259,7 +260,7 @@ impl RoutedPromiseListener<WebGPUDeviceResponse> for GPUAdapter {
&self.global(), &self.global(),
self.channel.clone(), self.channel.clone(),
self, self,
Heap::default(), HandleObject::null(),
wgpu_types::Features::default(), wgpu_types::Features::default(),
wgpu_types::Limits::default(), wgpu_types::Limits::default(),
device_id, device_id,

View file

@ -9,7 +9,7 @@ use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject}; use js::jsapi::{HandleObject, Heap, JSObject};
use webgpu_traits::{ use webgpu_traits::{
PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice, PopError, WebGPU, WebGPUComputePipeline, WebGPUComputePipelineResponse, WebGPUDevice,
WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline, WebGPUPoppedErrorScopeResponse, WebGPUQueue, WebGPURenderPipeline,
@ -114,7 +114,6 @@ impl GPUDevice {
fn new_inherited( fn new_inherited(
channel: WebGPU, channel: WebGPU,
adapter: &GPUAdapter, adapter: &GPUAdapter,
extensions: Heap<*mut JSObject>,
features: &GPUSupportedFeatures, features: &GPUSupportedFeatures,
limits: &GPUSupportedLimits, limits: &GPUSupportedLimits,
device: WebGPUDevice, device: WebGPUDevice,
@ -126,7 +125,7 @@ impl GPUDevice {
eventtarget: EventTarget::new_inherited(), eventtarget: EventTarget::new_inherited(),
channel, channel,
adapter: Dom::from_ref(adapter), adapter: Dom::from_ref(adapter),
extensions, extensions: Heap::default(),
features: Dom::from_ref(features), features: Dom::from_ref(features),
limits: Dom::from_ref(limits), limits: Dom::from_ref(limits),
label: DomRefCell::new(USVString::from(label)), label: DomRefCell::new(USVString::from(label)),
@ -142,7 +141,7 @@ impl GPUDevice {
global: &GlobalScope, global: &GlobalScope,
channel: WebGPU, channel: WebGPU,
adapter: &GPUAdapter, adapter: &GPUAdapter,
extensions: Heap<*mut JSObject>, extensions: HandleObject,
features: wgpu_types::Features, features: wgpu_types::Features,
limits: wgpu_types::Limits, limits: wgpu_types::Limits,
device: WebGPUDevice, device: WebGPUDevice,
@ -158,7 +157,6 @@ impl GPUDevice {
Box::new(GPUDevice::new_inherited( Box::new(GPUDevice::new_inherited(
channel, channel,
adapter, adapter,
extensions,
&features, &features,
&limits, &limits,
device, device,
@ -170,6 +168,7 @@ impl GPUDevice {
can_gc, can_gc,
); );
queue.set_device(&device); queue.set_device(&device);
device.extensions.set(*extensions);
device device
} }
} }

View file

@ -88,16 +88,15 @@ fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> Rethro
} }
#[derive(JSTraceable)] #[derive(JSTraceable)]
pub(crate) struct ModuleObject(Box<Heap<*mut JSObject>>); pub(crate) struct ModuleObject(RootedTraceableBox<Heap<*mut JSObject>>);
impl ModuleObject { impl ModuleObject {
fn new(obj: RustHandleObject) -> ModuleObject { fn new(obj: RustHandleObject) -> ModuleObject {
ModuleObject(Heap::boxed(obj.get())) ModuleObject(RootedTraceableBox::from_box(Heap::boxed(obj.get())))
} }
#[allow(unsafe_code)]
pub(crate) fn handle(&self) -> HandleObject { pub(crate) fn handle(&self) -> HandleObject {
unsafe { self.0.handle() } self.0.handle().into()
} }
} }

View file

@ -24,7 +24,7 @@ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{DomGlobal, DomObject}; use crate::dom::bindings::reflector::{DomGlobal, DomObject};
use crate::dom::bindings::root::Dom; use crate::dom::bindings::root::{AsHandleValue, Dom};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::csp::CspReporting; use crate::dom::csp::CspReporting;
use crate::dom::document::{ImageAnimationUpdateCallback, RefreshRedirectDue}; use crate::dom::document::{ImageAnimationUpdateCallback, RefreshRedirectDue};
@ -395,6 +395,7 @@ pub(crate) enum TimerCallback {
} }
#[derive(Clone, JSTraceable, MallocSizeOf)] #[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
enum InternalTimerCallback { enum InternalTimerCallback {
StringTimerCallback(DOMString), StringTimerCallback(DOMString),
FunctionTimerCallback( FunctionTimerCallback(
@ -416,6 +417,7 @@ impl Default for JsTimers {
impl JsTimers { impl JsTimers {
// see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn set_timeout_or_interval( pub(crate) fn set_timeout_or_interval(
&self, &self,
global: &GlobalScope, global: &GlobalScope,
@ -583,13 +585,8 @@ impl JsTimerTask {
} }
} }
// Returning Handles directly from Heap values is inherently unsafe, but here it's
// always done via rooted JsTimers, which is safe.
#[allow(unsafe_code)]
fn collect_heap_args<'b>(&self, args: &'b [Heap<JSVal>]) -> Vec<HandleValue<'b>> { fn collect_heap_args<'b>(&self, args: &'b [Heap<JSVal>]) -> Vec<HandleValue<'b>> {
args.iter() args.iter().map(|arg| arg.as_handle_value()).collect()
.map(|arg| unsafe { HandleValue::from_raw(arg.handle()) })
.collect()
} }
} }

View file

@ -955,39 +955,51 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if is_typed_array(type): if is_typed_array(type):
if failureCode is None: if failureCode is None:
unwrapFailureCode = (f'throw_type_error(*cx, "{sourceDescription} is not a typed array.");\n' unwrapFailureCode = (
f'{exceptionCode}') f'throw_type_error(*cx, "{sourceDescription} is not a typed array.");\n'
f'{exceptionCode}'
)
else: else:
unwrapFailureCode = failureCode unwrapFailureCode = failureCode
typeName = type.unroll().name # unroll because it may be nullable typeName = type.unroll().name # unroll because it may be nullable
if isMember == "Union": is_union_member = (isMember == "Union")
if is_union_member:
typeName = f"Heap{typeName}" typeName = f"Heap{typeName}"
map_call = ".map(RootedTraceableBox::new)"
else:
map_call = ""
templateBody = fill( templateBody = fill(
""" f"""
match typedarray::${ty}::from($${val}.get().to_object()) { match typedarray::${{ty}}::from($${{val}}.get().to_object()){map_call} {{
Ok(val) => val, Ok(val) => val,
Err(()) => { Err(()) => {{
$*{failureCode} $*{{failureCode}}
} }}
} }}
""", """,
ty=typeName, ty=typeName,
failureCode=f"{unwrapFailureCode}\n", failureCode=f"{unwrapFailureCode}\n",
) )
if isMember == "Union":
templateBody = f"RootedTraceableBox::new({templateBody})"
declType = CGGeneric(f"typedarray::{typeName}") declType = CGGeneric(f"typedarray::{typeName}")
if is_union_member:
declType = CGWrapper(declType, pre="RootedTraceableBox<", post=">")
if type.nullable(): if type.nullable():
templateBody = f"Some({templateBody})" templateBody = f"Some({templateBody})"
declType = CGWrapper(declType, pre="Option<", post=">") declType = CGWrapper(declType, pre="Option<", post=">")
templateBody = wrapObjectTemplate(templateBody, "None", templateBody = wrapObjectTemplate(
isDefinitelyObject, type, failureCode) templateBody,
"None",
isDefinitelyObject,
type,
failureCode,
)
return handleOptional(templateBody, declType, handleDefault("None")) return handleOptional(templateBody, declType, handleDefault("None"))