daniellansun commented on PR #2770:
URL: https://github.com/apache/groovy/pull/2770#issuecomment-5219797463

   
   Just a few thoughts after reading through the change. The idea makes a lot 
of sense to me — I only have some questions about how we keep the marker 
consistent and easy to maintain.
   
   ---
   
   ## What works well
   
   The problem is real: things the compiler (or transforms) call directly in 
the runtime are a compatibility surface that’s easy to miss. Classic call 
sites, `ScriptBytecodeAdapter`, indy bootstraps, transform helpers, 
GINQ/contracts/macro runtimes — all of that can outlive the compiler that 
produced the bytecode.
   
   A few things in this commit already feel right:
   
   - Putting `@GroovyABI` on the `ScriptBytecodeAdapter` *class*, then 
overriding only the newer methods (`compoundAssign`, `packedClosure`), is a 
nice pattern.
   - Touching the subprojects (callsite, contracts, ginq, macro) and not only 
groovy-core matches where those calls actually land.
   - Marking `CallSite` / `CallSiteArray` fits what we already say in 
`COMPATIBILITY.md` about classic linkage for Groovy 4/5 bytecode on 6.
   
   ---
   
   ## Annotation shape and docs
   
   ### Retention
   
   ```java
   @Retention(SOURCE)
   public @interface GroovyABI {
       String since() default "";
   }
   ```
   
   With `SOURCE` retention the marker never makes it into the published jars, 
so japicmp (and anything else that only sees class files) can’t use it. That 
might be exactly what you want if this is mainly for people reading the source 
— if so, saying that somewhere would help.
   
   If we later want a small check like “don’t drop a compiler-referenced member 
by accident,” `CLASS` or `RUNTIME` would make that easier (closer to how 
`@Incubating` works).
   
   It might also be worth a short note in `COMPATIBILITY.md` so `@GroovyABI` 
sits next to Public API / `@Internal` / the binary-compat check, rather than 
living only in the annotation Javadoc.
   
   ### `since`
   
   A few small inconsistencies stood out:
   
   - Five places on `InvokerHelper` use `since="?"`.
   - Version strings mix `1.0` / `1.0.0` and `2.0` / `2.0.0`.
   - Spacing varies: `since = "1.6.0"` vs `since="4.0.0"`.
   - The empty default (`default ""`) leaves it unclear when `since` is 
required.
   
   I’d lean toward one format (`x.y.z`), no `?` in the tree (either fill it in 
or leave `since` off until we know), and a short rule for how this relates to 
existing `@since` in Javadoc when both are present.
   
   ### Policy vs what’s annotated
   
   The Javadoc says `groovy.*` isn’t annotated because it’s already public API, 
but we do annotate members on `Closure`, `Reference`, and `Awaitable`.
   
   Either story is fine:
   
   - leave `groovy.*` alone and reserve `@GroovyABI` for internal/runtime types 
the compiler still hard-wires, or  
   - use `@GroovyABI` even on public types when the *compiler* depends on that 
specific member.
   
   I’d just pick one and make the text match the tree. Small Javadoc nits while 
we’re there: the notes use bare `<li>` without a list wrapper, and a `@since` 
on the annotation type itself would match the usual style.
   
   ---
   
   ## Class-level vs every method
   
   We already allow `TYPE`, and `ScriptBytecodeAdapter` uses that well. 
Elsewhere we stamp the same `since` on every overload:
   
   | Area | What happens |
   |---|---|
   | `Maps.of(...)` | ~100 identical annotations on a huge file |
   | `CallSite` | every `call*` / `callSafe*` / … overload |
   | `HashCodeHelper.updateHash` | every overload |
   | DTT box/unbox | every method; the class itself isn’t marked |
   
   Would it make sense to default to the type, and only mark members when 
`since` differs or when only part of the type is ABI? That would cut a lot of 
noise without losing the refinement pattern you already use on 
`ScriptBytecodeAdapter`. `Maps` is the clearest case — the per-overload marks 
don’t really say more than one class-level annotation would.
   
   ---
   
   ## How complete does the inventory need to be?
   
   A partial list is still useful as a start. The risk is that people read “no 
annotation” as “safe to change.” A few busy places show the gap:
   
   | Surface | Role | Currently |
   |---|---|---|
   | `ScriptBytecodeAdapter` | main classic bridge | class-level — good |
   | `DefaultTypeTransformation` | box/unbox **and** casts (`castToType`, etc.) 
| box/unbox only |
   | `NumberMath` | arithmetic / compare as well as bitwise | mostly bitwise / 
shift / mod |
   | `InvokerHelper` | property / invoke helpers | a subset; several with `?` |
   | `CallSiteArray` | classic linkage (ctor + `array` / `owner`) | constructor 
only |
   
   Also, two annotations sit on **private** methods, which outside bytecode 
can’t call:
   
   - `AssertionRenderer.render()` (private) — the public static `render(String, 
ValueRecorder)` is probably what we mean  
   - `FieldValues.findField(...)` (private helper)
   
   Those look accidental; moving them to the public entry points (or dropping 
them) would keep the meaning clear.
   
   ### Looking a bit further ahead
   
   Keeping hundreds of hand-placed marks up to date will be hard. A lot of this 
surface is already “registered” in the compiler via `MethodCaller` / 
multi-adapters, indy bootstraps, and transform-generated calls. Even a simple 
test that checks known registration points against `@GroovyABI` would help 
later. No need to build that now — just something to keep in mind so the first 
cut doesn’t fight that idea.
   
   For v1, two shapes both seem reasonable:
   
   1. **Narrow but complete** — e.g. call sites + `ScriptBytecodeAdapter` + 
indy bootstraps, fully covered; or  
   2. **Wide pass** like now, but with type-level defaults, no `?`, no private 
members, and a clear rule for what belongs (always-emitted bridges vs 
occasional DGM / static-compile paths).
   
   Right now a few DGM / `StringGroovyMethods` / `NumberMath` bits sit next to 
the core bridges without saying why those and not others. One sentence of 
guidance would go a long way so we don’t slowly mark half the GDK.
   
   ---
   
   ## A few edits that aren’t about `@GroovyABI`
   
   These are easy to miss in the same commit:
   
   | File | Change |
   |---|---|
   | `BinaryExpressionHelper` | drops an unused assignment after boxing |
   | `ClosureWriter` | unused `callX` import |
   | `StaticTypesCallSiteWriter` | whitespace on `"or"`, and **drops `case 
"implies":`** |
   
   The first two are harmless. The `implies` case changes STC dispatch for 
number×number `implies` (it no longer takes the optimized path). Even if that 
path is rare, it might be clearer in its own change — or with a short note and 
a test if we keep it here. Splitting pure inventory from compiler behaviour 
also makes history and reverts easier.
   
   ---
   
   ## Optional next steps
   
   Nothing urgent — more a suggested order if we iterate:
   
   1. Spell out the contract briefly in `COMPATIBILITY.md` (what it means, 
class vs member, version format, how it relates to public API).  
   2. Tighten the annotation (retention, `since` rules, Javadoc, `groovy.*` 
wording).  
   3. Prefer type-level marks when everything shares the same `since`.  
   4. Clean the inventory: resolve/drop `?`, fix private members, either finish 
the core bridges or document an explicit v1 subset.  
   5. Split (or call out) the unrelated compiler cleanups and the `implies` 
change.  
   6. Later: a lightweight check against known compiler registration points.
   
   ---
   
   ## Closing
   
   I like the direction. Making the compiler↔runtime surface visible will help 
future work. The main things I’d still like to settle are: who the annotation 
is for (humans only vs tooling), leaning on type-level marks so the inventory 
stays readable, and making the marked set either complete for a stated scope or 
clearly “work in progress,” so it stays a trustworthy signal.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to