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

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

jdaugherty commented on PR #2690:
URL: https://github.com/apache/groovy/pull/2690#issuecomment-4957813540

   On the drop return type note: 
   
   Because that "tie" is exactly the failure. Sorting only by name + parameters 
means a bridge method and the method it bridges compare equal (return 0). 
Arrays.sort is stable — TimSort — so equal elements keep their input order. And 
the input order is getDeclaredMethods(), the very thing that's nondeterministic 
and varies between JVM runs. So the tie doesn't get resolved; it silently 
inherits the nondeterminism you're trying to eliminate.
   
   Concretely, covariant returns / generic erasure produce this:
   
   class Base<T> { T get() { ... } }
   class Sub extends Base<String> {
       String get() { ... }   // real method
       // Object get()        // synthetic bridge — same name, same (empty) 
params
   }
   
   Sub has two get methods: same name, identical parameter types, differing 
only in return type (String vs Object). With a name+params comparator they tie, 
and whichever the JVM happened to enumerate first wins — per build. Adding the 
return-type key breaks that last tie, so no two distinct methods ever compare 
equal.
   
   That's what "total order" buys: the JVM forbids two methods in one class 
sharing a name and descriptor, and the descriptor is precisely parameters + 
return type. Compare on the full descriptor and every element has a unique sort 
key, so the output is fully determined no matter what order 
getDeclaredMethods() hands back. Leave out the return type and the ordering is 
merely partial — total everywhere except the bridge pairs, which are the one 
place reproducibility actually needed it.
   
   (Constructors don't need it: name is always <init> and return type always 
void, so parameters alone are a total order there — which is why 
getDeclaredConstructorsSorted stops at compareParameterTypes.)




> Nondeterministic reflection order flows into generated bytecode, breaking 
> reproducible builds
> ---------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12149
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12149
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: James Daugherty
>            Priority: Major
>
> Follow-up to GROOVY-12146, which fixed nondeterministic ordering of 
> annotation members copied from precompiled classes. The same root cause — 
> Class.getDeclaredMethods()/getDeclaredFields()/getDeclaredConstructors() 
> returning members in an unspecified order that varies between HotSpot runs — 
> also affects Java8#configureClassNode, which populates ClassNodes for 
> precompiled classes via reflection. The enumeration order is preserved 
> through to bytecode generation (e.g. via the LinkedHashMap returned by 
> ClassNode#getDeclaredMethodsMap), so two compilations of identical sources on 
> the same JDK can still produce byte-different class files.
> Two manifestations were observed while verifying the reproducibility of the 
> Apache Grails 8.0.0-M3 release artifacts (same sources, same JDK, 
> containerized double-build):
> 1. MOP bridge methods: for classes extending a precompiled Groovy class, 
> MopWriter#getSuperMethods iterates the superclass's declared-methods map, so 
> the synthetic super$N$… methods are emitted in a different order per build.
> 2. Woven trait methods: methods copied from a precompiled trait (e.g. GORM's 
> DirtyCheckable#trackChanges/syncChangedProperties) are woven into 
> implementing classes in enumeration order, reordering the emitted methods and 
> the constant pool.
> In both cases the bytecode is semantically identical — decompiled sources 
> match exactly — only member emission order (and the constant-pool layout that 
> follows from it) differs.



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

Reply via email to