James Daugherty created GROOVY-12149:
----------------------------------------
Summary: 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
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)