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

View file

@ -42,6 +42,7 @@ use js::typedarray::{
};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::trace::RootedTraceableBox;
#[cfg(feature = "webgpu")]
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::{CanGc, JSContext};
@ -52,15 +53,14 @@ use crate::script_runtime::{CanGc, JSContext};
/// provides a view onto an `ArrayBuffer`.
///
/// See: <https://webidl.spec.whatwg.org/#BufferSource>
#[derive(PartialEq)]
pub(crate) enum BufferSource {
/// Represents an `ArrayBufferView` (e.g., `Uint8Array`, `DataView`).
/// 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.
/// 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>(
@ -80,7 +80,7 @@ where
}
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
.buffer_source
{
BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => unsafe {
heap.handle() == from_heap.handle()
BufferSource::ArrayBufferView(from_heap) | BufferSource::ArrayBuffer(from_heap) => {
std::ptr::eq(heap.get(), from_heap.get())
},
},
}
@ -122,14 +122,16 @@ where
pub(crate) fn from_view(
chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
) -> HeapBufferSource<T> {
HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(unsafe {
*chunk.underlying_object()
})))
HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(RootedTraceableBox::from_box(
Heap::boxed(unsafe { *chunk.underlying_object() }),
)))
}
pub(crate) fn default() -> Self {
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,
}
}
@ -174,11 +176,11 @@ where
BufferSource::ArrayBufferView(buffer) => unsafe {
let mut is_shared = false;
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(
*view_buffer.handle(),
)))
HeapBufferSource::<ArrayBufferU8>::new(BufferSource::ArrayBuffer(
RootedTraceableBox::from_box(Heap::boxed(*view_buffer.handle())),
))
},
BufferSource::ArrayBuffer(_) => {
unreachable!("BufferSource::ArrayBuffer does not have a view buffer.")
@ -196,7 +198,7 @@ where
// assert buffer is an ArrayBuffer view
assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
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
debug_assert!(!is_shared);
// Detach the ArrayBuffer
@ -204,7 +206,7 @@ where
}
},
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 {
assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
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);
IsDetachedArrayBufferObject(*view_buffer.handle())
}
@ -245,7 +247,7 @@ where
unsafe {
assert!(JS_IsArrayBufferViewObject(*buffer.handle()));
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);
GetArrayBufferByteLength(*view_buffer.handle())
}
@ -429,13 +431,16 @@ where
) -> Option<HeapBufferSource<ArrayBufferU8>> {
match &self.buffer_source {
BufferSource::ArrayBufferView(heap) | BufferSource::ArrayBuffer(heap) => {
let result =
unsafe { ArrayBufferClone(*cx, heap.handle(), byte_offset, byte_length) };
let result = unsafe {
ArrayBufferClone(*cx, heap.handle().into(), byte_offset, byte_length)
};
if result.is_null() {
None
} else {
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 {
BufferSource::ArrayBufferView(from_heap) |
BufferSource::ArrayBuffer(from_heap) => {
unsafe {
if heap.handle() == from_heap.handle() {
return false;
}
};
if std::ptr::eq(heap.get(), from_heap.get()) {
return false;
}
},
}
},
@ -514,9 +517,9 @@ where
BufferSource::ArrayBufferView(from_heap) |
BufferSource::ArrayBuffer(from_heap) => ArrayBufferCopyData(
*cx,
heap.handle(),
heap.handle().into(),
dest_start,
from_heap.handle(),
from_heap.handle().into(),
from_byte_offset,
bytes_to_copy,
),
@ -541,7 +544,7 @@ where
let mut is_defined = false;
match &self.buffer_source {
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;
}
},
@ -568,7 +571,7 @@ where
// Step 2 (Reordered)
let buffer_data = match &self.buffer_source {
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 [[ArrayBufferByteLength]] internal slot value is arrayBufferByteLength.
Ok(HeapBufferSource::<ArrayBufferU8>::new(
BufferSource::ArrayBuffer(Heap::boxed(unsafe {
BufferSource::ArrayBuffer(RootedTraceableBox::from_box(Heap::boxed(unsafe {
NewArrayBufferWithContents(*cx, buffer_length, buffer_data)
})),
}))),
))
}
}
@ -679,9 +682,9 @@ pub(crate) fn create_buffer_source_with_constructor(
match &buffer_source.buffer_source {
BufferSource::ArrayBuffer(heap) => match constructor {
Constructor::DataView => Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
Heap::boxed(unsafe {
JS_NewDataView(*cx, heap.handle(), byte_offset, byte_length)
}),
RootedTraceableBox::from_box(Heap::boxed(unsafe {
JS_NewDataView(*cx, heap.handle().into(), byte_offset, byte_length)
})),
))),
Constructor::Name(name_type) => construct_typed_array(
cx,
@ -709,45 +712,78 @@ fn construct_typed_array(
BufferSource::ArrayBuffer(heap) => {
let array_view = unsafe {
match name_type {
Type::Int8 => {
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(
Type::Int8 => JS_NewInt8ArrayWithBuffer(
*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_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 => {
unreachable!("Invalid TypedArray type")
},
@ -755,7 +791,7 @@ fn construct_typed_array(
};
Ok(HeapBufferSource::new(BufferSource::ArrayBufferView(
Heap::boxed(array_view),
RootedTraceableBox::from_box(Heap::boxed(array_view)),
)))
},
BufferSource::ArrayBufferView(_) => {
@ -779,7 +815,7 @@ pub(crate) fn create_array_buffer_with_size(
Err(Error::Type("can't create array buffer".to_owned()))
} else {
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")]
#[derive(JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) struct DataView {
#[no_trace]
range: Range<usize>,

View file

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

View file

@ -30,7 +30,8 @@ use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
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 malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
pub(crate) use script_bindings::root::*;
@ -409,3 +410,21 @@ where
&*(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::jsval::{BooleanValue, JSVal, NullValue, ObjectValue, UndefinedValue};
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 super::bindings::trace::HashMapTracedValues;
@ -32,7 +32,7 @@ use crate::dom::bindings::error::{
};
use crate::dom::bindings::inheritance::Castable;
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::str::DOMString;
use crate::dom::document::Document;
@ -999,7 +999,6 @@ pub(crate) enum CustomElementReaction {
impl CustomElementReaction {
/// <https://html.spec.whatwg.org/multipage/#invoke-custom-element-reactions>
#[allow(unsafe_code)]
pub(crate) fn invoke(&self, element: &Element, can_gc: CanGc) {
// Step 2.1
match *self {
@ -1008,10 +1007,7 @@ impl CustomElementReaction {
},
CustomElementReaction::Callback(ref callback, ref arguments) => {
// We're rooted, so it's safe to hand out a handle to objects in Heap
let arguments = arguments
.iter()
.map(|arg| unsafe { HandleValue::from_raw(arg.handle()) })
.collect();
let arguments = arguments.iter().map(|arg| arg.as_handle_value()).collect();
rooted!(in(*GlobalScope::get_cx()) let mut value: JSVal);
let _ = callback.Call_(
element,
@ -1112,6 +1108,7 @@ impl CustomElementReactionStack {
}
/// <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(
&self,
element: &Element,

View file

@ -422,6 +422,7 @@ impl Element {
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>]>) {
self.ensure_rare_data()
.custom_element_reaction_queue

View file

@ -115,7 +115,7 @@ pub(crate) enum FileReaderReadyState {
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum FileReaderResult {
ArrayBuffer(#[ignore_malloc_size_of = "mozjs"] Heap<JSVal>),
ArrayBuffer(#[ignore_malloc_size_of = "mozjs"] RootedTraceableBox<Heap<JSVal>>),
String(DOMString),
}
@ -354,7 +354,8 @@ impl FileReader {
.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() {
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::inheritance::Castable;
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::structuredclone;
use crate::dom::event::Event;
@ -53,18 +53,18 @@ pub(crate) struct History {
impl History {
pub(crate) fn new_inherited(window: &Window) -> History {
let state = Heap::default();
state.set(NullValue());
History {
reflector_: Reflector::new(),
window: Dom::from_ref(window),
state,
state: Heap::default(),
state_id: Cell::new(None),
}
}
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>
/// Steps 5-16
#[allow(unsafe_code)]
pub(crate) fn activate_state(
&self,
state_id: Option<HistoryStateId>,
@ -145,7 +144,7 @@ impl History {
PopStateEvent::dispatch_jsval(
self.window.upcast::<EventTarget>(),
&self.window,
unsafe { HandleValue::from_raw(self.state.handle()) },
self.state.as_handle_value(),
can_gc,
);
}

View file

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

View file

@ -35,6 +35,7 @@ use script_bindings::conversions::SafeToJSValConvertible;
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorToJsval};
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::globalscope::GlobalScope;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
@ -435,7 +436,6 @@ struct WaitForAllFulfillmentHandler {
}
impl Callback for WaitForAllFulfillmentHandler {
#[allow(unsafe_code)]
fn callback(&self, _cx: SafeJSContext, v: HandleValue, _realm: InRealm, _can_gc: CanGc) {
// 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 equals_total {
// Safety: the values are kept alive by the Heap
// while their handles are passed to the the success steps.
let result_handles: Vec<HandleValue> = unsafe {
self.result
.borrow()
.iter()
.map(|val| HandleValue::from_raw(val.handle()))
.collect()
};
let result_ref = self.result.borrow();
let result_handles: Vec<HandleValue> =
result_ref.iter().map(|v| v.as_handle_value()).collect();
(self.success_steps)(result_handles);
}
}
@ -496,6 +491,7 @@ impl Callback for WaitForAllRejectionHandler {
}
/// <https://webidl.spec.whatwg.org/#wait-for-all>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn wait_for_all(
cx: SafeJSContext,
global: &GlobalScope,

View file

@ -154,7 +154,7 @@ pub(crate) struct PipeTo {
/// The error potentially passed to shutdown,
/// stored here because we must keep it across a microtask.
#[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.
/// 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
/// moved after it has been set.
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 {
unreachable!("Option set to Some(heap) above.");
};

View file

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

View file

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

View file

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

View file

@ -88,16 +88,15 @@ fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> Rethro
}
#[derive(JSTraceable)]
pub(crate) struct ModuleObject(Box<Heap<*mut JSObject>>);
pub(crate) struct ModuleObject(RootedTraceableBox<Heap<*mut JSObject>>);
impl 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 {
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::refcounted::Trusted;
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::csp::CspReporting;
use crate::dom::document::{ImageAnimationUpdateCallback, RefreshRedirectDue};
@ -395,6 +395,7 @@ pub(crate) enum TimerCallback {
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
enum InternalTimerCallback {
StringTimerCallback(DOMString),
FunctionTimerCallback(
@ -416,6 +417,7 @@ impl Default for JsTimers {
impl JsTimers {
// 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(
&self,
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>> {
args.iter()
.map(|arg| unsafe { HandleValue::from_raw(arg.handle()) })
.collect()
args.iter().map(|arg| arg.as_handle_value()).collect()
}
}

View file

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