[ 
https://issues.apache.org/jira/browse/GROOVY-12160?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18095830#comment-18095830
 ] 

ASF GitHub Bot commented on GROOVY-12160:
-----------------------------------------

codecov-commenter commented on PR #2705:
URL: https://github.com/apache/groovy/pull/2705#issuecomment-4956213370

   ## 
[Codecov](https://app.codecov.io/gh/apache/groovy/pull/2705?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
 Report
   :white_check_mark: All modified and coverable lines are covered by tests.
   :white_check_mark: Project coverage is 69.1132%. Comparing base 
([`86c6648`](https://app.codecov.io/gh/apache/groovy/commit/86c6648d1e61436fe513afaa85d8d0c403de3cda?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache))
 to head 
([`72d9bb9`](https://app.codecov.io/gh/apache/groovy/commit/72d9bb94b81cf85e085d6f181e4c773e045749ea?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
   
   <details><summary>Additional details and impacted files</summary>
   
   
   
   [![Impacted file tree 
graph](https://app.codecov.io/gh/apache/groovy/pull/2705/graphs/tree.svg?width=650&height=150&src=pr&token=1r45138NfQ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)](https://app.codecov.io/gh/apache/groovy/pull/2705?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   
   ```diff
   @@                Coverage Diff                 @@
   ##               master      #2705        +/-   ##
   ==================================================
   + Coverage     69.1077%   69.1132%   +0.0054%     
   - Complexity      34235      34236         +1     
   ==================================================
     Files            1537       1537                
     Lines          129356     129356                
     Branches        23503      23503                
   ==================================================
   + Hits            89395      89402         +7     
   + Misses          31941      31933         -8     
   - Partials         8020       8021         +1     
   ```
   
   | [Files with missing 
lines](https://app.codecov.io/gh/apache/groovy/pull/2705?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
 | Coverage Δ | |
   |---|---|---|
   | 
[...groovy/transform/AnnotationCollectorTransform.java](https://app.codecov.io/gh/apache/groovy/pull/2705?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fcodehaus%2Fgroovy%2Ftransform%2FAnnotationCollectorTransform.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvY29kZWhhdXMvZ3Jvb3Z5L3RyYW5zZm9ybS9Bbm5vdGF0aW9uQ29sbGVjdG9yVHJhbnNmb3JtLmphdmE=)
 | `90.6404% <100.0000%> (ø)` | |
   
   ... and [8 files with indirect coverage 
changes](https://app.codecov.io/gh/apache/groovy/pull/2705/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   </details>
   <details><summary> :rocket: New features to boost your workflow: </summary>
   
   - :snowflake: [Test 
Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, 
report on failures, and find test suite problems.
   - :package: [JS Bundle 
Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save 
yourself from yourself by tracking and limiting bundle sizes in JS merges.
   </details>




> @AnnotationCollector emits collected annotation members in hash order when 
> the collector is precompiled
> -------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12160
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12160
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>
> h2. Description
> When a collector annotation is precompiled, {{AnnotationCollectorTransform}} 
> reads its preset members back from the generated {{CollectorHelper}} and 
> copies them onto the annotations the collector expands to. That copy goes 
> through an unordered map:
> {code:java}
> // AnnotationCollectorTransform.java:316-337 (makeListOfAnnotations)
> for (Object[] inner : data) {
>     Class<?> anno = (Class<?>) inner[0];
>     AnnotationNode toAdd = new AnnotationNode(ClassHelper.make(anno));
>     ret.add(toAdd);
>     Map<String,Object> member = (Map<String, Object>) inner[1];
>     if (member.isEmpty()) {
>         continue;
>     }
>     Map<String, Expression> generated = new HashMap<>(member.size());
>     for (Map.Entry<String, Object> entry : member.entrySet()) {
>         generated.put(entry.getKey(), makeExpression(entry.getValue()));
>     }
>     copyMembers(generated, toAdd);       // iterates `generated` -> 
> toAdd.addMember(...)
> }
> {code}
> {{copyMembers}} ({{:274}}) iterates that map into 
> {{AnnotationNode.addMember}}, and those members are written to the class 
> file's annotation attribute in that order. So *the members of the expanded 
> annotation are emitted in the hash order of their names*.
> Declaration order is intact everywhere else in the chain and is lost only 
> here: {{serialize(AnnotationNode)}} ({{:184}}) builds the helper's map by 
> iterating {{AnnotationNode.getMembers()}}, which is a {{LinkedHashMap}}, so 
> the order the members were declared on the collector survives into the 
> precompiled {{CollectorHelper}}.
> h3. Reachability
> Only when the collector is *precompiled* — i.e. resolved from the classpath, 
> so the members are read reflectively from its {{CollectorHelper}} via 
> {{getTargetListFromClass}} ({{:280-290}}). A collector declared in the same 
> compilation unit takes the AST-based path, which preserves order. The 
> precompiled case is the normal one for any collector shipped in a library, 
> including Groovy's own {{@Immutable}} and {{@Canonical}}.
> h3. Demonstration
> Compilation unit 1 (the collector, presetting five members):
> {code:java}
> import groovy.transform.AnnotationCollector
> import java.lang.annotation.*
> @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
> @interface Marker {
>     String alpha() default ''
>     String beta() default ''
>     String gamma() default ''
>     String delta() default ''
>     String epsilon() default ''
> }
> @Marker(alpha='a', beta='b', gamma='c', delta='d', epsilon='e')
> @AnnotationCollector
> @interface Coll {}
> {code}
> Compilation unit 2, compiled against the first:
> {code:java}
> @Coll
> class Holder {}
> {code}
> Reading the emitted {{Holder.class}} with an ASM {{ClassReader}}, the members 
> of the {{@Marker}} annotation appear as:
> {noformat}
> delta, epsilon, beta, gamma, alpha
> {noformat}
> rather than the declared order {{alpha, beta, gamma, delta, epsilon}}.
> h3. Impact
> The keys are {{String}}s and {{String.hashCode}} is stable, so for a given 
> key set the iteration order is reproducible across JVM runs — this is not by 
> itself a live reproducible-builds failure. It still matters because the 
> emitted order is arbitrary rather than meaningful, it lands in the 
> {{RuntimeVisibleAnnotations}} attribute of the class file, and within a 
> colliding bucket {{HashMap}} chain order follows insertion order. Of the 
> related ordering issues below, this one has the most ordinary trigger: any 
> classpath collector with more than one preset member.
> h3. Proposed fix
> Use an insertion-ordered map:
> {code:java}
> Map<String, Expression> generated = new LinkedHashMap<>(member.size());
> {code}
> The collected members are then emitted in the order they were declared on the 
> collector. The member *set* and their values are unchanged — only the order.
> h3. Test
> {{src/test/groovy/org/codehaus/groovy/transform/AnnotationCollectorMemberOrderTest.groovy}}
>  compiles the annotation and collector into a temp directory as one 
> compilation unit, applies the collector from a second unit with that 
> directory on the classpath (so the precompiled path is exercised), then reads 
> the emitted bytes with an ASM {{ClassReader}} and asserts the annotation's 
> members appear in declaration order. It fails on the current code with 
> {{['delta', 'epsilon', 'beta', 'gamma', 'alpha']}} and passes with the fix.
> h3. Related
> GROOVY-12149 (nondeterministic reflection order flows into generated 
> bytecode). Same class of defect: {{Verifier.addCovariantMethods}} (covariant 
> bridge methods emitted in hash order), 
> {{AutoImplementASTTransformation.getAllCorrectedMethodsMap}} 
> ({{@AutoImplement}} methods emitted in hash order), {{AsmClassGenerator}} 
> {{referencedClasses}} ({{$class$}} synthetic fields and accessors), and 
> GROOVY-12156 ({{chooseBestMethod}} candidates in identity-hash order).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to