[
https://issues.apache.org/jira/browse/GROOVY-11796?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Valentine updated GROOVY-11796:
-------------------------------
Description:
After upgrading to Groovy 5.0.x, the CompilationUnit.getClasses() method
returns classes in an incorrect order when traits with closures are used.
When compiling a trait with a closure and a class that implements this trait,
getClasses() returns classes in an order where the implementing class comes
BEFORE the trait class.
This causes ClassNotFoundException when trying to define classes sequentially
in a ClassLoader.
*Steps to reproduce*
this code with closure FAILS
{code:java}
trait MyTrait {
MyTrait findByName(String name) {
return values().find { it.name() == name }
}
}
enum MyEnum implements MyTrait {
A, B, C
}
{code}
*Actual order from getClasses()*
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_findByName_closure1
2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
3 = MyTrait
*This code without closure WORKS*
{code:java}
trait MyTrait {
MyTrait findByName(String name) {
for (def value : values()) {
if (value.name() == name){
return value
}
}
return null
}
}
enum MyEnum implements MyTrait {
A, B, C
}
{code}
*Actual order from getClasses()*
0 = MyTrait
1 = MyTrait$Trait$Helper
2 = MyEnum ← CORRECT: MyEnum comes AFTER MyTrait
IMO this is regression because:
1. {*}Groovy 4.0.28{*}: This code worked correctly - classes were returned in
proper dependency order.
2. {*}Groovy 5.0.0 and 5.0.1{*}: Even simple cases like
trait A {}
class B implements A{}
returned incorrect order.
3. {*}Groovy 5.0.2{*}: Simple cases were fixed, but the problem still persists
when closures are used in traits.
Additionally, this code WORKS and getClasses() provides *correct* order.
{code:java}
trait MyTrait {
static String test(int x) {
def result
switch {
case 1: result = 'one'; break
default: result = 'other'; break
}
return result
}
}
enum MyEnum implements MyTrait
{ A, B }
{code}
{*}This code doesn't work{*}.
{code:java}
trait MyTrait {
static String test(int x) {
def result = switch {
case 1 -> 'one'
default -> 'other'
}
return result
}
}
enum MyEnum implements MyTrait { A, B }
{code}
getClasses() provides this order of classes.
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_test_closure1
2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
3 = MyTrait
This code *doesn't work* either
{code:java}
trait MyTrait {
def process() {
values().stream()
.filter(v -> v.name() == 'A')
.findFirst()
}
}
enum MyEnum implements MyTrait
{ A, B }
{code}
The same problem with the order.
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_process_closure1
2 = MyEnum
3 = MyTrait
was:
After upgrading to Groovy 5.0.x, the CompilationUnit.getClasses() method
returns classes in an incorrect order when traits with closures are used.
When compiling a trait with a closure and a class that implements this trait,
getClasses() returns classes in an order where the implementing class comes
BEFORE the trait class.
This causes ClassNotFoundException when trying to define classes sequentially
in a ClassLoader.
*Steps to reproduce*
this code with closure FAILS
{code:java}
trait MyTrait {
MyTrait findByName(String name) {
return values().find { it.name() == name }
}
}
enum MyEnum implements MyTrait {
A, B, C
}
{code}
*Actual order from getClasses()*
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_findByName_closure1
2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
3 = MyTrait
*This code without closure WORKS*
{code:java}
trait MyTrait {
MyTrait findByName(String name) {
for (def value : values()) {
if (value.name() == name){
return value
}
}
return null
}
}
enum MyEnum implements MyTrait {
A, B, C
}
{code}
*Actual order from getClasses()*
0 = MyTrait
1 = MyTrait$Trait$Helper
2 = MyEnum ← CORRECT: MyEnum comes AFTER MyTrait
IMO this is regression because:
1. {*}Groovy 4.0.28{*}: This code worked correctly - classes were returned in
proper dependency order.
2. {*}Groovy 5.0.0 and 5.0.1{*}: Even simple cases like
trait A {}
class B implements A{}
returned incorrect order.
3. {*}Groovy 5.0.2{*}: Simple cases were fixed, but the problem still persists
when closures are used in traits.
Additionally, this code WORKS and getClasses() provides *correct* order.
{code:java}
trait MyTrait {
static String test(int x) {
def result
switch {
case 1: result = 'one'; break
default: result = 'other'; break
}
return result
}
}
enum MyEnum implements MyTrait
{ A, B }
{code}
{*}This code doesn't work{*}.
{code:java}
trait MyTrait {
static String test(int x) {
def result = switch {
case 1 -> 'one'
default -> 'other'
}
return result
}
}
enum MyEnum implements MyTrait { A, B }
{code}
getClasses() provides this order of classes.
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_test_closure1
2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
3 = MyTrait
This code *doesn't work* either
{code:java}
trait MyTrait {
def process() {
values().stream()
.filter(v -> v.name() == 'A')
.findFirst()
}
}
enum MyEnum implements MyTrait
{ A, B }
{code}
The same problem with the order.
0 = MyTrait$Trait$Helper
1 = MyTrait$Trait$Helper$_process_closure1
2 = MyEnum
3 = MyTrait
> CompilationUnit.getClasses() returns classes in incorrect order when trait
> with closure is used
> ------------------------------------------------------------------------------------------------
>
> Key: GROOVY-11796
> URL: https://issues.apache.org/jira/browse/GROOVY-11796
> Project: Groovy
> Issue Type: Bug
> Components: groovy-jdk
> Affects Versions: 5.0.0, 5.0.1, 5.0.2
> Environment: Java version: OpenJDK 21
> OS: Linux
> Reporter: Valentine
> Priority: Major
> Attachments: TraitOrderTest.java
>
>
> After upgrading to Groovy 5.0.x, the CompilationUnit.getClasses() method
> returns classes in an incorrect order when traits with closures are used.
>
> When compiling a trait with a closure and a class that implements this trait,
> getClasses() returns classes in an order where the implementing class comes
> BEFORE the trait class.
> This causes ClassNotFoundException when trying to define classes sequentially
> in a ClassLoader.
> *Steps to reproduce*
> this code with closure FAILS
>
> {code:java}
> trait MyTrait {
> MyTrait findByName(String name) {
> return values().find { it.name() == name }
> }
> }
> enum MyEnum implements MyTrait {
> A, B, C
> }
> {code}
>
> *Actual order from getClasses()*
> 0 = MyTrait$Trait$Helper
> 1 = MyTrait$Trait$Helper$_findByName_closure1
> 2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
> 3 = MyTrait
>
> *This code without closure WORKS*
>
> {code:java}
> trait MyTrait {
> MyTrait findByName(String name) {
> for (def value : values()) {
> if (value.name() == name){
> return value
> }
> }
> return null
> }
> }
>
> enum MyEnum implements MyTrait {
> A, B, C
> }
> {code}
>
>
> *Actual order from getClasses()*
> 0 = MyTrait
> 1 = MyTrait$Trait$Helper
> 2 = MyEnum ← CORRECT: MyEnum comes AFTER MyTrait
>
> IMO this is regression because:
> 1. {*}Groovy 4.0.28{*}: This code worked correctly - classes were returned in
> proper dependency order.
> 2. {*}Groovy 5.0.0 and 5.0.1{*}: Even simple cases like
> trait A {}
> class B implements A{}
> returned incorrect order.
> 3. {*}Groovy 5.0.2{*}: Simple cases were fixed, but the problem still
> persists when closures are used in traits.
> Additionally, this code WORKS and getClasses() provides *correct* order.
>
> {code:java}
> trait MyTrait {
> static String test(int x) {
> def result
> switch {
> case 1: result = 'one'; break
> default: result = 'other'; break
> }
> return result
> }
> }
> enum MyEnum implements MyTrait
> { A, B }
> {code}
>
> {*}This code doesn't work{*}.
>
> {code:java}
> trait MyTrait {
> static String test(int x) {
> def result = switch {
> case 1 -> 'one'
> default -> 'other'
> }
> return result
> }
> }
> enum MyEnum implements MyTrait { A, B }
> {code}
>
> getClasses() provides this order of classes.
> 0 = MyTrait$Trait$Helper
> 1 = MyTrait$Trait$Helper$_test_closure1
> 2 = MyEnum ← PROBLEM: MyEnum comes BEFORE MyTrait
> 3 = MyTrait
> This code *doesn't work* either
>
> {code:java}
> trait MyTrait {
> def process() {
> values().stream()
> .filter(v -> v.name() == 'A')
> .findFirst()
> }
> }
> enum MyEnum implements MyTrait
> { A, B }
> {code}
>
> The same problem with the order.
> 0 = MyTrait$Trait$Helper
> 1 = MyTrait$Trait$Helper$_process_closure1
> 2 = MyEnum
> 3 = MyTrait
--
This message was sent by Atlassian Jira
(v8.20.10#820010)