Skip to content

Commit 1765a7c

Browse files
authored
Support rooting arbitrary types (#535)
* Support rooting optional Traceable values. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Separate heap barrier operations from notion of safe initialization. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Mark Rooted/RootedGuard/CustomAutoRooter/CustomAutoRooterGuard with crown annotations. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Re-export TraceableTrace for custom rootable types. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Move Traceable trait to mozjs-sys. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Rename TraceableTrace to Rootable, and reduce overlap with Traceable. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Add test for new Rootable usage. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Formatting. Signed-off-by: Josh Matthews <josh@joshmatthews.net> --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
1 parent e68fdba commit 1765a7c

File tree

12 files changed

+622
-503
lines changed

12 files changed

+622
-503
lines changed

mozjs-sys/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ doctest = false
2626
debugmozjs = []
2727
profilemozjs = []
2828
jitspew = []
29+
crown = []
2930

3031
[dependencies]
3132
libc.workspace = true

mozjs-sys/src/jsgc.rs

Lines changed: 97 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
use crate::glue::CallPropertyDescriptorTracer;
6-
use crate::jsapi::js::TraceValueArray;
75
use crate::jsapi::JS;
86
use crate::jsapi::{jsid, JSFunction, JSObject, JSScript, JSString, JSTracer};
9-
107
use crate::jsid::VoidId;
118
use std::cell::UnsafeCell;
129
use std::ffi::{c_char, c_void};
@@ -68,9 +65,9 @@ impl RootKind for JS::Value {
6865
const KIND: JS::RootKind = JS::RootKind::Value;
6966
}
7067

71-
impl<T: TraceableTrace> RootKind for T {
68+
impl<T: Rootable> RootKind for T {
7269
type Vtable = *const RootedVFTable;
73-
const VTABLE: Self::Vtable = &<Self as TraceableTrace>::VTABLE;
70+
const VTABLE: Self::Vtable = &<Self as Rootable>::VTABLE;
7471
const KIND: JS::RootKind = JS::RootKind::Traceable;
7572
}
7673

@@ -93,32 +90,25 @@ impl RootedVFTable {
9390
pub const PADDING: [usize; 2] = [0, 0];
9491
}
9592

96-
/// `Rooted<T>` with a T that uses the Traceable RootKind uses dynamic dispatch on the C++ side
97-
/// for custom tracing. This trait provides trace logic via a vtable when creating a Rust instance
98-
/// of the object.
99-
pub unsafe trait TraceableTrace: Sized {
93+
/// Marker trait that allows any type that implements the [trace::Traceable] trait to be used
94+
/// with the [Rooted] type.
95+
///
96+
/// `Rooted<T>` relies on dynamic dispatch in C++ when T uses the Traceable RootKind.
97+
/// This trait initializes the vtable when creating a Rust instance of the Rooted object.
98+
pub trait Rootable: crate::trace::Traceable + Sized {
10099
const VTABLE: RootedVFTable = RootedVFTable {
101100
padding: RootedVFTable::PADDING,
102-
trace: Self::trace,
101+
trace: <Self as Rootable>::trace,
103102
};
104103

105104
unsafe extern "C" fn trace(this: *mut c_void, trc: *mut JSTracer, _name: *const c_char) {
106105
let rooted = this as *mut Rooted<Self>;
107106
let rooted = rooted.as_mut().unwrap();
108-
Self::do_trace(&mut rooted.ptr, trc);
107+
<Self as crate::trace::Traceable>::trace(&mut rooted.ptr, trc);
109108
}
110-
111-
/// Used by `TraceableTrace` implementer to trace its contents.
112-
/// Corresponds to virtual `trace` call in a `Rooted` that inherits from
113-
/// StackRootedTraceableBase (C++).
114-
unsafe fn do_trace(&mut self, trc: *mut JSTracer);
115109
}
116110

117-
unsafe impl TraceableTrace for JS::PropertyDescriptor {
118-
unsafe fn do_trace(&mut self, trc: *mut JSTracer) {
119-
CallPropertyDescriptorTracer(trc, self);
120-
}
121-
}
111+
impl<T: Rootable> Rootable for Option<T> {}
122112

123113
// The C++ representation of Rooted<T> inherits from StackRootedBase, which
124114
// contains the actual pointers that get manipulated. The Rust representation
@@ -135,37 +125,63 @@ pub struct RootedBase {
135125

136126
// Annoyingly, bindgen can't cope with SM's use of templates, so we have to roll our own.
137127
#[repr(C)]
138-
#[derive(Debug)]
128+
#[cfg_attr(
129+
feature = "crown",
130+
crown::unrooted_must_root_lint::allow_unrooted_interior
131+
)]
139132
pub struct Rooted<T: RootKind> {
140133
pub vtable: T::Vtable,
141134
pub base: RootedBase,
142135
pub ptr: T,
143136
}
144137

138+
/// Trait that provides a GC-safe default value for the given type, if one exists.
139+
pub trait Initialize: Sized {
140+
/// Create a default value. If there is no meaningful default possible, returns None.
141+
/// SAFETY:
142+
/// The default must not be a value that can be meaningfully garbage collected.
143+
unsafe fn initial() -> Option<Self>;
144+
}
145+
146+
impl<T> Initialize for Option<T> {
147+
unsafe fn initial() -> Option<Self> {
148+
Some(None)
149+
}
150+
}
151+
145152
/// A trait for types which can place appropriate GC barriers.
146153
/// * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/Garbage_collection#Incremental_marking
147154
/// * https://dxr.mozilla.org/mozilla-central/source/js/src/gc/Barrier.h
148-
pub trait GCMethods {
155+
pub trait GCMethods: Initialize {
149156
/// Create a default value
150-
unsafe fn initial() -> Self;
157+
unsafe fn initial() -> Self {
158+
<Self as Initialize>::initial()
159+
.expect("Types used with heap GC methods must have a valid default")
160+
}
151161

152162
/// Place a post-write barrier
153163
unsafe fn post_barrier(v: *mut Self, prev: Self, next: Self);
154164
}
155165

156-
impl GCMethods for *mut JSObject {
157-
unsafe fn initial() -> *mut JSObject {
158-
ptr::null_mut()
166+
impl Initialize for *mut JSObject {
167+
unsafe fn initial() -> Option<*mut JSObject> {
168+
Some(ptr::null_mut())
159169
}
170+
}
171+
172+
impl GCMethods for *mut JSObject {
160173
unsafe fn post_barrier(v: *mut *mut JSObject, prev: *mut JSObject, next: *mut JSObject) {
161174
JS::HeapObjectWriteBarriers(v, prev, next);
162175
}
163176
}
164177

165-
impl GCMethods for *mut JSFunction {
166-
unsafe fn initial() -> *mut JSFunction {
167-
ptr::null_mut()
178+
impl Initialize for *mut JSFunction {
179+
unsafe fn initial() -> Option<*mut JSFunction> {
180+
Some(ptr::null_mut())
168181
}
182+
}
183+
184+
impl GCMethods for *mut JSFunction {
169185
unsafe fn post_barrier(v: *mut *mut JSFunction, prev: *mut JSFunction, next: *mut JSFunction) {
170186
JS::HeapObjectWriteBarriers(
171187
mem::transmute(v),
@@ -175,60 +191,83 @@ impl GCMethods for *mut JSFunction {
175191
}
176192
}
177193

178-
impl GCMethods for *mut JSString {
179-
unsafe fn initial() -> *mut JSString {
180-
ptr::null_mut()
194+
impl Initialize for *mut JSString {
195+
unsafe fn initial() -> Option<*mut JSString> {
196+
Some(ptr::null_mut())
181197
}
198+
}
199+
200+
impl GCMethods for *mut JSString {
182201
unsafe fn post_barrier(v: *mut *mut JSString, prev: *mut JSString, next: *mut JSString) {
183202
JS::HeapStringWriteBarriers(v, prev, next);
184203
}
185204
}
186205

187-
impl GCMethods for *mut JS::Symbol {
188-
unsafe fn initial() -> *mut JS::Symbol {
189-
ptr::null_mut()
206+
impl Initialize for *mut JS::Symbol {
207+
unsafe fn initial() -> Option<*mut JS::Symbol> {
208+
Some(ptr::null_mut())
190209
}
210+
}
211+
212+
impl GCMethods for *mut JS::Symbol {
191213
unsafe fn post_barrier(_: *mut *mut JS::Symbol, _: *mut JS::Symbol, _: *mut JS::Symbol) {}
192214
}
193215

194-
impl GCMethods for *mut JS::BigInt {
195-
unsafe fn initial() -> *mut JS::BigInt {
196-
ptr::null_mut()
216+
impl Initialize for *mut JS::BigInt {
217+
unsafe fn initial() -> Option<*mut JS::BigInt> {
218+
Some(ptr::null_mut())
197219
}
220+
}
221+
222+
impl GCMethods for *mut JS::BigInt {
198223
unsafe fn post_barrier(v: *mut *mut JS::BigInt, prev: *mut JS::BigInt, next: *mut JS::BigInt) {
199224
JS::HeapBigIntWriteBarriers(v, prev, next);
200225
}
201226
}
202227

203-
impl GCMethods for *mut JSScript {
204-
unsafe fn initial() -> *mut JSScript {
205-
ptr::null_mut()
228+
impl Initialize for *mut JSScript {
229+
unsafe fn initial() -> Option<*mut JSScript> {
230+
Some(ptr::null_mut())
206231
}
232+
}
233+
234+
impl GCMethods for *mut JSScript {
207235
unsafe fn post_barrier(v: *mut *mut JSScript, prev: *mut JSScript, next: *mut JSScript) {
208236
JS::HeapScriptWriteBarriers(v, prev, next);
209237
}
210238
}
211239

212-
impl GCMethods for jsid {
213-
unsafe fn initial() -> jsid {
214-
VoidId()
240+
impl Initialize for jsid {
241+
unsafe fn initial() -> Option<jsid> {
242+
Some(VoidId())
215243
}
244+
}
245+
246+
impl GCMethods for jsid {
216247
unsafe fn post_barrier(_: *mut jsid, _: jsid, _: jsid) {}
217248
}
218249

219-
impl GCMethods for JS::Value {
220-
unsafe fn initial() -> JS::Value {
221-
JS::Value::default()
250+
impl Initialize for JS::Value {
251+
unsafe fn initial() -> Option<JS::Value> {
252+
Some(JS::Value::default())
222253
}
254+
}
255+
256+
impl GCMethods for JS::Value {
223257
unsafe fn post_barrier(v: *mut JS::Value, prev: JS::Value, next: JS::Value) {
224258
JS::HeapValueWriteBarriers(v, &prev, &next);
225259
}
226260
}
227261

228-
impl GCMethods for JS::PropertyDescriptor {
229-
unsafe fn initial() -> JS::PropertyDescriptor {
230-
JS::PropertyDescriptor::default()
262+
impl Rootable for JS::PropertyDescriptor {}
263+
264+
impl Initialize for JS::PropertyDescriptor {
265+
unsafe fn initial() -> Option<JS::PropertyDescriptor> {
266+
Some(JS::PropertyDescriptor::default())
231267
}
268+
}
269+
270+
impl GCMethods for JS::PropertyDescriptor {
232271
unsafe fn post_barrier(
233272
_: *mut JS::PropertyDescriptor,
234273
_: JS::PropertyDescriptor,
@@ -258,19 +297,14 @@ impl<const N: usize> ValueArray<N> {
258297
}
259298
}
260299

261-
unsafe impl<const N: usize> TraceableTrace for ValueArray<N> {
262-
unsafe fn do_trace(&mut self, trc: *mut JSTracer) {
263-
TraceValueArray(trc, N, self.get_mut_ptr());
264-
}
265-
}
300+
impl<const N: usize> Rootable for ValueArray<N> {}
266301

267-
impl<const N: usize> GCMethods for ValueArray<N> {
268-
unsafe fn initial() -> Self {
269-
Self {
270-
elements: [JS::Value::initial(); N],
271-
}
302+
impl<const N: usize> Initialize for ValueArray<N> {
303+
unsafe fn initial() -> Option<Self> {
304+
Some(Self {
305+
elements: [<JS::Value as GCMethods>::initial(); N],
306+
})
272307
}
273-
unsafe fn post_barrier(_: *mut Self, _: Self, _: Self) {}
274308
}
275309

276310
/// RootedValueArray roots an internal fixed-size array of Values
@@ -371,7 +405,7 @@ impl<T: GCMethods + Copy> Drop for Heap<T> {
371405
fn drop(&mut self) {
372406
unsafe {
373407
let ptr = self.ptr.get();
374-
T::post_barrier(ptr, *ptr, T::initial());
408+
T::post_barrier(ptr, *ptr, <T as GCMethods>::initial());
375409
}
376410
}
377411
}

mozjs-sys/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
#![allow(unused_extern_crates)]
6+
#![cfg_attr(feature = "crown", feature(register_tool))]
7+
#![cfg_attr(feature = "crown", register_tool(crown))]
68

79
// These extern crates are needed for linking
810
extern crate encoding_c;
@@ -18,12 +20,14 @@ pub mod glue;
1820
pub mod jsgc;
1921
pub mod jsid;
2022
pub mod jsval;
23+
pub mod trace;
2124

2225
// Reexport the bindings in the jsapi module
2326
pub use crate::generated::root as jsapi;
2427

2528
// The bindings generated by bindgen
2629
#[doc(hidden)]
30+
#[allow(dead_code)]
2731
mod generated {
2832
include!(concat!(env!("OUT_DIR"), "/build/jsapi.rs"));
2933
}

0 commit comments

Comments
 (0)