[
https://issues.apache.org/jira/browse/GROOVY-12544?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King resolved GROOVY-12544.
--------------------------------
Resolution: Fixed
> InnerClasses attribute omits nested classes that are not in the current
> compilation unit
> ----------------------------------------------------------------------------------------
>
> Key: GROOVY-12544
> URL: https://issues.apache.org/jira/browse/GROOVY-12544
> Project: Groovy
> Issue Type: Improvement
> Components: Compiler
> Affects Versions: 4.0.33, 5.1.3, 6.0.0
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
> Fix For: 7.0.0-alpha-1, 5.1.4, 6.0.1
>
>
> groovyc writes an {{InnerClasses}} entry for a referenced nested class only
> when that class is in the same compilation unit. A nested class read from the
> classpath, whether a pre-compiled Groovy class or a JDK class such as
> {{Map.Entry}}, gets no entry. javac writes an entry for every nested class
> referenced from the class.
> h3. Reproducer
> {code:groovy}
> // A.groovy
> class A { static class B {} }
> // C.groovy
> class C { def m() { new A.B() } }
> {code}
> {noformat}
> groovyc -d together A.groovy C.groovy
> groovyc -d first A.groovy
> groovyc -d second -cp first C.groovy
> {noformat}
> {{together/C.class}} contains:
> {noformat}
> InnerClasses:
> public static #34= #31 of #33; // B=class A$B of class A
> {noformat}
> {{second/C.class}} has no {{InnerClasses}} attribute. The extra constant-pool
> entries in the first case also shift later indices, so instructions can
> change too, e.g. {{ldc}} becomes {{ldc_w}}.
> For comparison, javac compiling
> {code:java}
> class Y { Object m(java.util.Map.Entry e) { return new
> java.util.AbstractMap.SimpleEntry(1, 2); } }
> {code}
> writes entries for both {{AbstractMap$SimpleEntry}} and {{Map$Entry}}. The
> equivalent Groovy class has none.
> h3. Why it matters
> * *Reproducible output:* compiling an unchanged source file gives different
> bytes depending on what else is in the compilation unit. Incremental builds
> recompile a subset of sources against the previous output, so unchanged
> classes come out different.
> * *Downstream recompilation:* in the Gradle project from GROOVY-10932
> (https://github.com/jdaugherty/groovy-trait-incremental-compile), with the
> GROOVY-10932 fix applied, the missing {{InnerClasses}} entry for a trait
> helper is the only remaining difference in the implementer. It is still
> enough to make the downstream project recompile. A control run where the
> class came out byte-identical left the downstream compile up to date. Trait
> helpers are only one case: any class referencing a nested type behaves the
> same way.
> * *Spec:* JVMS ยง4.7.6 expects every nested class referenced from the constant
> pool to be listed in {{InnerClasses}}.
> Runtime behaviour does not appear to be affected. Reflection on a nested
> class ({{getDeclaringClass}}, {{getSimpleName}}, ...) reads that class's own
> {{InnerClasses}} attribute, not the attribute of the classes that reference
> it.
> h3. Scripts
> Scripts behave the same way. A script body that references a nested class
> gets an {{InnerClasses}} entry only when that class is in the same
> compilation unit. Classes declared alongside the script body in a script file
> are top-level siblings rather than nested classes, so they need no entry and
> do not drift. Classes nested inside those siblings do drift, like any other
> nested class. No special handling for scripts is needed.
> h3. Possible approach
> * *Decompiler:* {{AsmDecompiler}} already reads a class's self-referencing
> {{InnerClasses}} entry to get its modifiers. It could also keep the outer
> class name and simple name in {{ClassStub}}.
> * *Class generator:* {{AsmClassGenerator.maybeInnerClassEntry}} currently
> writes an entry only when {{getOuterClass() != null}}, which holds only for
> {{InnerClassNode}}s. It could also write one for a member class read from the
> classpath, recursing through enclosing classes as {{makeInnerClassEntry}}
> does today (GROOVY-9842). For class nodes built by reflection,
> {{Class.getDeclaringClass()}} provides the same information.
> * *Keep {{getOuterClass()}} unchanged:* it should keep returning {{null}} for
> classpath classes. Name resolution, static type checking and access checks
> rely on it, so the change should stay confined to bytecode generation.
> h3. Compatibility
> This changes the bytes of most class files that reference a nested type,
> including commonly used JDK types such as {{Map.Entry}}. Behaviour is not
> expected to change, but builds comparing outputs across versions will see
> differences. I suggest master only, with a note in the release notes.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)