[
https://issues.apache.org/jira/browse/GROOVY-10932?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18134050#comment-18134050
]
Paul King commented on GROOVY-10932:
------------------------------------
Thanks [~jdaugherty], the analysis holds up. I can reproduce both problems on
master with plain groovyc, no Gradle involved:
* *Bytecode drift:* an implementer compiled against a pre-compiled trait
carries {{@Flag(note="", value=true)}} where a same-unit compile carries
{{@Flag}}. It also lacks the {{InnerClasses}} entry for {{Named$Trait$Helper}}.
* *The original NoClassDefFoundError:* compile everything, delete
{{MyTest.class}} and {{MyTestBase.class}}, then recompile just those two
against the output. The compile fails with {{Unable to configure
WithPluginValidation$AllPluginsValidation due to missing dependency
MyTestBase}}. On master this needs the implementer to be {{@CompileStatic}}; in
dynamic mode nothing happens to inspect that class closely enough.
Both have the cause you identified. {{Traits.findHelpers}} loads a pre-compiled
trait's helper classes through the class loader and builds them by reflection,
while every other classpath type is read from its class file.
*Fix:* when the trait itself was read from bytecode (the default), its helpers
are now resolved through the same reference resolver, so they are read from
their class files too. Traits that were loaded as classes keep the old lookup.
This removes the NoClassDefFoundError and the annotation-default drift. Tests
for both are included. The fix is in [PR
#3076|https://github.com/apache/groovy/pull/3076].
Compile time shows no measurable difference: 600 implementers of 20
pre-compiled traits compile in the same time with and without the fix. Fewer
classes get loaded into the compiler's JVM, since the helpers no longer are.
Method and annotation-member ordering stays deterministic: class-file order,
plus the existing GROOVY-11674 sort.
*Two differences remain:*
# *{{InnerClasses}} entries.* These are not specific to traits. groovyc writes
an {{InnerClasses}} entry for a referenced nested class only when that class is
in the same compilation unit, whereas javac always writes one. For example,
{{class C { def m() { new A.B() } }}} compiles differently depending on whether
{{A}} is compiled alongside it. In your Gradle demo, this entry alone is enough
to make {{:app}} recompile: with the fix applied, {{app}} still recompiles. A
control run where {{Thing.class}} came out byte-identical left {{app}} up to
date. Fixing it changes the bytecode of most classes that reference a nested
type, so I'll raise it as a separate issue for master.
# *Parameter names* in the local-variable debug info of forwarder methods
differ unless the trait is compiled with {{--parameters}}
({{groovyOptions.parameters = true}} in Gradle). With it, both paths agree.
These names live inside method bodies, so they shouldn't affect ABI-based
compile avoidance.
The plan is master and 6.0.x for this fix. 5.1.x would need a hand-port, so
I'll do that only if someone is hitting the crash there.
> Groovy incremental compilation failure with traits
> --------------------------------------------------
>
> Key: GROOVY-10932
> URL: https://issues.apache.org/jira/browse/GROOVY-10932
> Project: Groovy
> Issue Type: Bug
> Components: Compiler
> Affects Versions: 3.0.15
> Reporter: Lóránt Pintér
> Priority: Major
>
> See reproducer here:
> [https://github.com/asodja/groovy-traits-compilation-reproducer]
> This reproduces issue with Groovy traits and Gradle incremental compilation.
> Steps to reproduce:
> 1. git clone [email protected]:asodja/groovy-traits-compilation-reproducer.git
> 2. run `./gradlew :lib:compileGroovy --no-build-cache`
> 3. Change `println("a")` in MyTest.groovy to `println("b")`
> 4. run `./gradlew :lib:compileGroovy --no-build-cache` again
> 5. See error
> To resolve the issue run `./gradlew :lib:compileGroovy --no-build-cache
> --rerun-tasks`
> Exception thrown:
> {code:java}
> java.lang.RuntimeException: java.lang.NoClassDefFoundError: Unable to load
> class test.groovy.traits.WithPluginValidation$AllPluginsValidation due to
> missing dependency Ltest/groovy/traits/MyTestBase;
> at
> org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:977)
> at
> org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:672)
> at
> org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:636)
> at
> org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:611)
> at
> org.gradle.api.internal.tasks.compile.ApiGroovyCompiler.execute(ApiGroovyCompiler.java:271)
> ...
> Caused by: java.lang.NoClassDefFoundError: Unable to load class
> test.groovy.traits.WithPluginValidation$AllPluginsValidation due to missing
> dependency Ltest/groovy/traits/MyTestBase;
> at org.codehaus.groovy.vmplugin.v8.Java8.configureClassNode(Java8.java:447)
> at org.codehaus.groovy.ast.ClassNode.lazyClassInit(ClassNode.java:273)
> at
> org.codehaus.groovy.ast.ClassNode.getUnresolvedSuperClass(ClassNode.java:1014)
> at
> org.codehaus.groovy.ast.ClassNode.getUnresolvedSuperClass(ClassNode.java:1006)
> at org.codehaus.groovy.ast.ClassNode.getSuperClass(ClassNode.java:1000)
> at org.codehaus.groovy.ast.ClassNode.isDerivedFrom(ClassNode.java:911)
> at
> org.codehaus.groovy.classgen.asm.InvocationWriter.castToNonPrimitiveIfNecessary(InvocationWriter.java:1144)
> at
> org.codehaus.groovy.classgen.asm.OperandStack.doConvertAndCast(OperandStack.java:363)
> at
> org.codehaus.groovy.classgen.asm.OperandStack.doGroovyCast(OperandStack.java:300)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.storeThisInstanceField(AsmClassGenerator.java:1259)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.visitFieldExpression(AsmClassGenerator.java:1196)
> at org.codehaus.groovy.ast.expr.FieldExpression.visit(FieldExpression.java:41)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.visitPropertyExpression(AsmClassGenerator.java:1124)
> at
> org.codehaus.groovy.ast.expr.PropertyExpression.visit(PropertyExpression.java:63)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.visitVariableExpression(AsmClassGenerator.java:1339)
> at
> org.codehaus.groovy.ast.expr.VariableExpression.visit(VariableExpression.java:71)
> ...
> at
> org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:52)
> at
> org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:273)
> at
> org.codehaus.groovy.control.CompilationUnit$3.call(CompilationUnit.java:798)
> at
> org.codehaus.groovy.control.CompilationUnit$IPrimaryClassNodeOperation.doPhaseOperation(CompilationUnit.java:943)
> ... 34 more
> {code}
> Classes hierarchy:
> {code:java}
> class MyTest extends MyTestBase implements WithPluginValidation { ... }
> class MyTestBase { ... } // has a dependency to MyTest inside a
> method@SelfType(MyTestBase)
> trait WithPluginValidation {
> final AllPluginsValidation allPlugins = new AllPluginsValidation(this)
> static class AllPluginsValidation {
> final MyTestBase base
> AllPluginsValidation(MyTestBase base) {
> this.base = base
> }
> }
> }{code}
>
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)