[
https://issues.apache.org/jira/browse/GROOVY-12159?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18095824#comment-18095824
]
ASF GitHub Bot commented on GROOVY-12159:
-----------------------------------------
codecov-commenter commented on PR #2704:
URL: https://github.com/apache/groovy/pull/2704#issuecomment-4956001362
##
[Codecov](https://app.codecov.io/gh/apache/groovy/pull/2704?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.1054%. 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
([`e49d82f`](https://app.codecov.io/gh/apache/groovy/commit/e49d82fb7e832d51b3a157d153f12fd0f731d9a1?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>
[](https://app.codecov.io/gh/apache/groovy/pull/2704?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
```diff
@@ Coverage Diff @@
## master #2704 +/- ##
==================================================
- Coverage 69.1077% 69.1054% -0.0023%
- Complexity 34235 34237 +2
==================================================
Files 1537 1537
Lines 129356 129356
Branches 23503 23503
==================================================
- Hits 89395 89392 -3
- Misses 31941 31943 +2
- Partials 8020 8021 +1
```
| [Files with missing
lines](https://app.codecov.io/gh/apache/groovy/pull/2704?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
| Coverage Δ | |
|---|---|---|
|
[...rg/codehaus/groovy/classgen/AsmClassGenerator.java](https://app.codecov.io/gh/apache/groovy/pull/2704?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fcodehaus%2Fgroovy%2Fclassgen%2FAsmClassGenerator.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvY29kZWhhdXMvZ3Jvb3Z5L2NsYXNzZ2VuL0FzbUNsYXNzR2VuZXJhdG9yLmphdmE=)
| `85.0616% <100.0000%> (ø)` | |
... and [4 files with indirect coverage
changes](https://app.codecov.io/gh/apache/groovy/pull/2704/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>
> Synthetic class-literal fields and accessors are emitted in hash order, not
> the order the literals were encountered
> -------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12159
> URL: https://issues.apache.org/jira/browse/GROOVY-12159
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
>
> h2. Description
> {{AsmClassGenerator}} collects the class literals that cannot be emitted as
> an {{ldc}} into an unordered map, and iterates it to generate the synthetic
> helper members:
> {code:java}
> // AsmClassGenerator.java:284
> private final Map<String,ClassNode> referencedClasses = new HashMap<>();
> // AsmClassGenerator.java:1857 (visitClassExpression)
> String staticFieldName = getStaticFieldName(type);
> referencedClasses.put(staticFieldName, type);
> // AsmClassGenerator.java:1774 (createSyntheticStaticFields)
> for (Map.Entry<String, ClassNode> entry : referencedClasses.entrySet()) {
> ... // <-- emits the $class$... field and its $get$$class$... accessor
> }
> {code}
> The keys are the synthetic field names, so *the emitted {{$class$...}} fields
> and their {{$get$$class$...}} accessors appear in the hash order of those
> names* rather than in the order the class literals were encountered. Both
> iteration sites are affected: {{createSyntheticStaticFields}} ({{:1774}}) for
> classes and {{createInterfaceSyntheticStaticFields}} ({{:1758}}) for
> interfaces.
> h3. Reachability
> This path is narrow. {{visitClassExpression}} emits an {{ldc}} — and never
> touches the map — unless {{BytecodeHelper.isClassLiteralPossible(type)}} is
> false and the type is not in the same compilation unit. That predicate only
> checks for {{public}}:
> {code:java}
> // BytecodeHelper.java:660
> public static boolean isClassLiteralPossible(ClassNode classNode) {
> return Modifier.isPublic(classNode.getModifiers());
> }
> {code}
> So the map is populated only when a class literal names a *non-public type
> from another compilation unit*. Ordinary code never reaches it. This is the
> least impactful of the related ordering issues below, and worth fixing mainly
> for consistency and because the fix is a single word.
> h3. Demonstration
> Compilation unit 1:
> {code:java}
> package p
> import groovy.transform.PackageScope
> @PackageScope class Ha {}
> @PackageScope class Hb {}
> @PackageScope class Hc {}
> @PackageScope class Hd {}
> @PackageScope class He {}
> {code}
> Compilation unit 2, compiled against the first:
> {code:java}
> class User {
> def f() { [p.Ha, p.Hb, p.Hc, p.Hd, p.He] }
> }
> {code}
> Reading the emitted {{User.class}} with an ASM {{ClassReader}}, the synthetic
> members appear as:
> {noformat}
> fields : $class$p$Ha, $class$p$Hc, $class$p$Hb, $class$p$He, $class$p$Hd
> accessors: $get$$class$p$Ha, $get$$class$p$Hc, $get$$class$p$Hb,
> $get$$class$p$He, $get$$class$p$Hd
> {noformat}
> rather than the encounter order {{Ha, Hb, Hc, Hd, He}}.
> 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, and because within a
> colliding bucket {{HashMap}} chain order follows insertion order, which is
> the order the literals are visited.
> h3. Proposed fix
> Use an insertion-ordered map:
> {code:java}
> private final Map<String,ClassNode> referencedClasses = new LinkedHashMap<>();
> {code}
> The synthetic fields and accessors are then emitted in the order the class
> literals were encountered. The member *set* is unchanged — only the order.
> The map is already {{clear()}}ed per class in {{visitClass}} ({{:355}}), so
> ordering remains scoped to each class.
> h3. Test
> {{src/test/groovy/org/codehaus/groovy/classgen/SyntheticClassLiteralFieldOrderTest.groovy}}
> compiles the {{@PackageScope}} types into a temp directory as one
> compilation unit, compiles the referencing class as a second unit with that
> directory on the classpath, then reads the emitted bytes with an ASM
> {{ClassReader}} and asserts both the {{$class$...}} fields and the
> {{$get$$class$...}} accessors appear in encounter order. It fails on the
> current code with {{['Ha', 'Hc', 'Hb', 'He', 'Hd']}} 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),
> {{AnnotationCollectorTransform.makeListOfAnnotations}} (annotation member
> order for precompiled collectors), and GROOVY-12156 ({{chooseBestMethod}}
> candidates in identity-hash order).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)