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:
Samson 2023-08-04 12:17:43 +02:00 committed by GitHub
parent 66e0d543cf
commit 9514f670d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
167 changed files with 1903 additions and 1020 deletions

View file

@ -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]