Scott Murphy Heiberg created GROOVY-12712:
---------------------------------------------
Summary: for await names its source variable with a JVM-wide
counter, so class files depend on compile order
Key: GROOVY-12712
URL: https://issues.apache.org/jira/browse/GROOVY-12712
Project: Groovy
Issue Type: Bug
Components: parser-antlr4
Affects Versions: 6.0.0
Environment: Groovy 6.0.0 binary distribution and master 7379c5720d;
BellSoft JDK 21.0.12; macOS 15 arm64
Reporter: Scott Murphy Heiberg
Since 6.0.0 the parser desugars a {{for await}} loop into a block that declares
the iterable in a variable named {{$__forAwaitSource_<n>}}
({{AsyncTransformHelper.wrapForAwaitLoop}}, called from
{{AstBuilder.visitForStmtAlt}}). The number comes from a {{static AtomicLong}}
shared by every compilation in the JVM, not from the source unit, so the same
file compiles to different class bytes depending on what was compiled before it
in the same {{groovyc}}, build daemon or IDE session (the name reaches the
local variable table), and two parses of one file in one JVM give two different
ASTs.
Reproducer (6.0.0, JDK 21):
{code:groovy}
// Beta.groovy
class Beta {
static List<String> names() {
def gen = async { for (String s in ['a', 'b']) { yield return s } }
List<String> seen = []
for await (n in gen) { seen << (n as String) }
seen
}
}
{code}
{{Alpha.groovy}} is any other class with one {{for await}} loop.
{noformat}
$ groovyc -d one Beta.groovy && javap -p -l -cp one Beta | grep -o
'\$__forAwaitSource_[0-9]*' | sort -u
$__forAwaitSource_1
$ groovyc -d two Alpha.groovy Beta.groovy && javap -p -l -cp two Beta | grep -o
'\$__forAwaitSource_[0-9]*' | sort -u
$__forAwaitSource_2
$ cmp one/Beta.class two/Beta.class
one/Beta.class two/Beta.class differ: char 1743, line 9
{noformat}
Consequences: non-reproducible builds; build caches and incremental compilers
keyed on outputs see a change that is not in the source; and tools that compare
the CONVERSION-phase AST of a file with a re-parse of it (a formatter checking
that it preserved the AST) report a difference for every file with a {{for
await}} loop. The other names generated for {{async}} and {{yield return}} were
stable across parses.
Suggested fix: number the loops per source unit in {{AstBuilder}} and pass the
number to {{wrapForAwaitLoop}}, so the name depends on the source alone. Pull
request to follow.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)