mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
No tracing of nop traceable fields (#29926)
* Add `no_trace` option to JSTraceable derive * NoTrace wrapper * Port some types to no_trace schematics * Fixing my unsafe mistakes (not tracing traceables) * Add docs & safety guards for no_trace Safety guards (trait shenanigans) guarantees safety usage of `no_trace` * Port canvas_traits to no_trace * Port servo_media to no_trace * Port net_traits to no_trace * Port style to no_trace * Port webgpu to no_trace * Port script_traits to no_trace * Port canvas_traits, devtools_traits, embedder_traits, profile_traits to no_trace * unrooted_must_root lint in seperate file * Add trace_in_no_trace_lint as script_plugin * Composable types in must_not_have_traceable * Introduced HashMapTracedValues wrapper * `HashMap<NoTrace<K>,V>`->`HashMapTracedValues<K,V>` * Port rest of servo's types to no_trace * Port html5ever, euclid, mime and http to no_trace * Port remaining externals to no_trace * Port webxr and Arc<Mutex<_>> * Fix spelling in notrace doc
This commit is contained in:
parent
66e0d543cf
commit
9514f670d1
167 changed files with 1903 additions and 1020 deletions
|
@ -7,10 +7,160 @@ extern crate syn;
|
|||
#[macro_use]
|
||||
extern crate synstructure;
|
||||
|
||||
decl_derive!([JSTraceable] => js_traceable_derive);
|
||||
decl_derive!([JSTraceable, attributes(no_trace)] =>
|
||||
/// Implements `JSTraceable` on structs and enums
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// #[derive(JSTraceable)]
|
||||
/// struct S {
|
||||
/// js_managed: JSManagedType,
|
||||
/// #[no_trace]
|
||||
/// non_js: NonJSManagedType,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// creates:
|
||||
///
|
||||
/// ```rust
|
||||
/// unsafe impl JSTraceable for S {
|
||||
/// #[inline]
|
||||
/// unsafe fn trace(&self, tracer: *mut js::jsapi::JSTracer) {
|
||||
/// match *self {
|
||||
/// S {
|
||||
/// js_managed: ref __binding_0,
|
||||
/// non_js: ref __binding_1,
|
||||
/// } => {
|
||||
/// {
|
||||
/// __binding_0.trace(tracer);
|
||||
/// }
|
||||
/// {
|
||||
/// // __binding_1 is not traceable so we do not need to trace it
|
||||
/// }
|
||||
/// },
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In cases where there is a need to make type (empty) traceable (`HashMap<NoTraceable, Traceable>`),
|
||||
/// NoTrace wrapper can be used, because it implements empty traceble:
|
||||
/// ```rust
|
||||
/// unsafe impl<T> JSTraceable for NoTrace<T> {
|
||||
/// unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) { /* nop */}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## SAFETY
|
||||
/// Puting `#[no_trace]` on fields is safe if there are no types that are JS managed in that field.
|
||||
/// `#[no_trace]` should NOT be put on field that does implement (non-empty) `JSTraceable` (is JS managed).
|
||||
/// There are safeguards in place to prevent such mistakes. Example error:
|
||||
///
|
||||
/// ```console
|
||||
/// error[E0282]: type annotations needed
|
||||
/// |
|
||||
/// | #[derive(JSTraceable, MallocSizeOf)]
|
||||
/// | ^^^^^^^^^^^ cannot infer type of the type parameter `Self` declared on the trait `NoTraceOnJSTraceable`
|
||||
/// |
|
||||
/// = note: this error originates in the derive macro `JSTraceable`
|
||||
/// ```
|
||||
///
|
||||
/// If you can assure that type has empty JSTraceable impl, you can bypass guards, providing your reasoning:
|
||||
/// ```rust
|
||||
/// #[derive(JSTraceable)]
|
||||
/// struct S {
|
||||
/// #[no_trace = "Safe because both u32 and u64 are empty traceable"]
|
||||
/// field: HashMap<u32, u64>,
|
||||
/// }
|
||||
/// ```
|
||||
js_traceable_derive);
|
||||
|
||||
// based off https://docs.rs/static_assertions/latest/src/static_assertions/assert_impl.rs.html#263
|
||||
fn assert_not_impl_traceable(ty: &syn::Type) -> proc_macro2::TokenStream {
|
||||
quote!(
|
||||
const _: fn() = || {
|
||||
// Generic trait with a blanket impl over `()` for all types.
|
||||
// becomes ambiguous if impl
|
||||
trait NoTraceOnJSTraceable<A> {
|
||||
// Required for actually being able to reference the trait.
|
||||
fn some_item() {}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> NoTraceOnJSTraceable<()> for T {}
|
||||
|
||||
// Used for the specialized impl when JSTraceable is implemented.
|
||||
#[allow(dead_code)]
|
||||
struct Invalid0;
|
||||
// forbids JSTraceable
|
||||
impl<T> NoTraceOnJSTraceable<Invalid0> for T where T: ?Sized + crate::dom::bindings::trace::JSTraceable {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Invalid2;
|
||||
// forbids HashMap<JSTraceble, _>
|
||||
impl<K, V, S> NoTraceOnJSTraceable<Invalid2> for std::collections::HashMap<K, V, S>
|
||||
where
|
||||
K: crate::dom::bindings::trace::JSTraceable + std::cmp::Eq + std::hash::Hash,
|
||||
S: std::hash::BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Invalid3;
|
||||
// forbids HashMap<_, JSTraceble>
|
||||
impl<K, V, S> NoTraceOnJSTraceable<Invalid3> for std::collections::HashMap<K, V, S>
|
||||
where
|
||||
K: std::cmp::Eq + std::hash::Hash,
|
||||
V: crate::dom::bindings::trace::JSTraceable,
|
||||
S: std::hash::BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Invalid4;
|
||||
// forbids BTreeMap<_, JSTraceble>
|
||||
impl<K, V> NoTraceOnJSTraceable<Invalid4> for std::collections::BTreeMap<K, V> where
|
||||
K: crate::dom::bindings::trace::JSTraceable + std::cmp::Eq + std::hash::Hash
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Invalid5;
|
||||
// forbids BTreeMap<_, JSTraceble>
|
||||
impl<K, V> NoTraceOnJSTraceable<Invalid5> for std::collections::BTreeMap<K, V>
|
||||
where
|
||||
K: std::cmp::Eq + std::hash::Hash,
|
||||
V: crate::dom::bindings::trace::JSTraceable,
|
||||
{
|
||||
}
|
||||
|
||||
// If there is only one specialized trait impl, type inference with
|
||||
// `_` can be resolved and this can compile. Fails to compile if
|
||||
// ty implements `NoTraceOnJSTraceable<InvalidX>`.
|
||||
let _ = <#ty as NoTraceOnJSTraceable<_>>::some_item;
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
fn js_traceable_derive(s: synstructure::Structure) -> proc_macro2::TokenStream {
|
||||
let match_body = s.each(|binding| Some(quote!(#binding.trace(tracer);)));
|
||||
let mut asserts = quote!();
|
||||
let match_body = s.each(|binding| {
|
||||
for attr in binding.ast().attrs.iter() {
|
||||
match attr.parse_meta().unwrap() {
|
||||
syn::Meta::Path(ref path) | syn::Meta::List(syn::MetaList { ref path, .. }) => {
|
||||
if path.is_ident("no_trace") {
|
||||
asserts.extend(assert_not_impl_traceable(&binding.ast().ty));
|
||||
return None;
|
||||
}
|
||||
},
|
||||
syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) => {
|
||||
if path.is_ident("no_trace") {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(quote!(#binding.trace(tracer);))
|
||||
});
|
||||
|
||||
let ast = s.ast();
|
||||
let name = &ast.ident;
|
||||
|
@ -24,6 +174,8 @@ fn js_traceable_derive(s: synstructure::Structure) -> proc_macro2::TokenStream {
|
|||
}
|
||||
|
||||
let tokens = quote! {
|
||||
#asserts
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl #impl_generics crate::dom::bindings::trace::JSTraceable for #name #ty_generics #where_clause {
|
||||
#[inline]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue