Conversation
|
LGTM |
ff76bff to
05b4eea
Compare
guybedford
left a comment
There was a problem hiding this comment.
I'm sorry I just don't think this is the right approach - matching name strings is clearly in violation of scoping rules, as I've mentioned before.
Please explain to me why we aren't using a trait to handle this?
26d1598 to
5580e83
Compare
5580e83 to
974acac
Compare
|
Addressed the review feedback:
Could you please take another look? |
| // Licensed under the Apache 2.0 license found in the LICENSE file or at: | ||
| // https://opensource.org/licenses/Apache-2.0 | ||
|
|
||
| //! Procedural macros for the JSG (JavaScript Glue) Rust bindings. |
There was a problem hiding this comment.
| //! Procedural macros for the JSG (JavaScript Glue) Rust bindings. | |
| //! Procedural macros for the JSG Rust bindings. |
jasnell
left a comment
There was a problem hiding this comment.
Findings from review:
- [MEDIUM]
name+custom_tracecombination silently drops the name override —extract_name_attributeparses the full attr stream as a singleMetaNameValue, which fails whencustom_traceis also present - [MEDIUM]
has_custom_trace_flaguses string-based parsing rather thansynmeta parsing, which is inconsistent with the rest of the crate - [LOW]
RefCell<T>tracing usesself.borrow()which will panic if a mutable borrow is active during GC
This review was generated by an AI assistant and may contain inaccuracies.
| let class_name = if attr.is_empty() { | ||
| name.to_string() | ||
| } else { | ||
| extract_name_attribute(attr).unwrap_or_else(|| name.to_string()) |
There was a problem hiding this comment.
When attr is name = "Foo", custom_trace, has_custom_trace_flag on line 39 correctly detects the flag, but extract_name_attribute(attr) here tries to parse the entire token stream as a single syn::MetaNameValue. That parse fails on the trailing , custom_trace, causing .ok()? to return None — the name override is silently dropped and the struct's Rust name is used instead.
The README (line 403) documents this combination as supported: #[jsg_resource(name = "MyName", custom_trace)].
Consider stripping custom_trace from the token stream before passing to extract_name_attribute, or refactoring the attribute parsing to handle all flags in one pass (similar to parse_jsg_property_args).
| /// Handles both bare `custom_trace` and combined forms like `name = "Foo", custom_trace`. | ||
| /// When set, `#[jsg_resource]` on a struct suppresses the auto-generated `GarbageCollected` | ||
| /// impl, letting the user write their own. | ||
| pub fn has_custom_trace_flag(attr: &TokenStream) -> bool { |
There was a problem hiding this comment.
Given this PR's motivation (replacing string-based type matching with trait-driven tracing), it's a bit inconsistent that has_custom_trace_flag itself converts the TokenStream to a string and splits on ,. Consider using syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated to look for a bare Path ident custom_trace — this would be consistent with parse_jsg_property_args and other attribute parsing in this crate. Not blocking since the current approach works and has test coverage.
|
|
||
| impl<T: Traced> Traced for std::cell::RefCell<T> { | ||
| fn trace(&self, visitor: &mut crate::v8::GcVisitor) { | ||
| self.borrow().trace(visitor); |
There was a problem hiding this comment.
RefCell::borrow() will panic if the RefCell is currently mutably borrowed. If GC tracing fires while a mutable borrow is active, this panics — and since panics across FFI are UB, this could abort.
In practice, GC tracing happens at well-defined points where user code isn't running, so this is unlikely to be hit. But consider either:
- Using
try_borrow()and silently skipping the trace if the borrow fails, or - Adding a doc comment noting that
RefCellfields in resources must not be mutably borrowed during GC
(The Cell<T> impl above correctly avoids this issue by using as_ptr().)
guybedford
left a comment
There was a problem hiding this comment.
Please can we just add one more thing to this PR - a derive macro for Traced on structs and enums?
Example:
#[derive(jsg::Traced)]
struct MyPlainData { ... }
#[derive(jsg::Traced)]
enum MyPlainData { ... }
basically works just like the current trace.rs does anyway.
| //! implement `jsg::Traced`, and the generated body simply delegates to | ||
| //! `jsg::Traced::trace(&self.field, visitor)` for each named field. | ||
|
|
||
| /// Generates one trace delegation statement per named field. |
There was a problem hiding this comment.
It could be nice to just explain the design a little more clearly here, something like:
| /// Generates one trace delegation statement per named field. | |
| /// Generates one trace delegation statement per named field. | |
| /// All fields are traced for safety by default, based on enforcing they implement | |
| /// the `Traced` trait, which all `GarbageCollected` types implement, and all non-GC | |
| /// types implement as a no-op. |
| impl Traced for &str {} | ||
| impl Traced for Number {} |
There was a problem hiding this comment.
Put these into impl_traced_noop above?
guybedford
left a comment
There was a problem hiding this comment.
Marking this as conditional approval on the comments and derive macro being added.
Addresses the following comments by @jasnell: