codeconsole opened a new issue, #16755:
URL: https://github.com/apache/grails-core/issues/16755
To start in a native image without recorded metadata, the framework
registers a JDK proxy for every interface a closure is coerced to
(`BytecodeRuntimeHints`, added in #16180: the `coercedTo` loop in `register`).
Its javadoc gives the reason. A closure that becomes an interface, whether it
is passed where a method takes one or coerced with `as`, becomes it through a
`java.lang.reflect.Proxy` created as the call is made. An image creates a proxy
only for an interface it was told about. That holds even in statically compiled
code: `IntStream.range(0, n).reduce(0) { int a, int b -> a + b }` in a
`@CompileStatic` class compiles to an invokedynamic `cast` to
`IntBinaryOperator`, and the runtime makes the proxy.
apache/groovy#3026 adds an incubating compiler option,
`-Dgroovy.target.closure.sam=true`, that converts such closures at compile
time. With it, a statically compiled closure literal that is passed, assigned,
returned, cast or coerced with `as` to a functional interface is wrapped in a
class the compiler generates instead of a proxy. When the arguments already
have the types the closure declares, the call reaches the closure without
reflection either. In a rough JVM loop, two interface calls went from about 26
ns to 2.8 ns. Groovy leaves the option off by default because the converted
value is no longer a `java.lang.reflect.Proxy`.
The scan needs no change. A conversion compiled with the option no longer
contains the invokedynamic `cast` or `asType` call the scan looks for, so no
proxy is registered for it. What still converts at run time keeps its
registration: dynamic code, maps coerced with `as`, trait or abstract-class
targets, and closures chosen by a conditional or Elvis expression.
## What to change once a Groovy release with apache/groovy#3026 is in use
- [ ] Turn the option on for the framework's own Groovy compiles: add
`-Dgroovy.target.closure.sam=true` to the forked compiler's JVM arguments in
`CompilePlugin.configureCompiler` (build-logic), beside
`-Dgrails.isolated.build=true`.
- [ ] Decide whether the Grails Gradle plugin turns it on for applications,
from `GrailsGradlePlugin` beside the other `forkOptions.jvmArgumentProviders`
of the `GroovyCompile` tasks, with a `grails { }` switch to turn it off. Since
the option is incubating and changes the class of a converted value, it could
start on for native and AOT builds only.
- [ ] Update `BytecodeRuntimeHintsSpec`. Its statically compiled closure
fixtures will no longer be converted through a proxy:
- `ClosureCoercionFixture.sum` no longer contains the invokedynamic
`cast`, so 'an interface a closure is coerced to can be proxied' fails;
- `StaticCoercionFixture.closureLiteral()` returns the generated class, so
the row 'a closure literal in statically compiled code' of 'the interface
#coercion is coerced to can be proxied' fails on `Proxy.isProxyClass`.
Invert both, so they assert that no proxy is registered or created, and
they then pin that the option is in effect. The map row and the dynamic rows
stay as they are.
- [ ] Update the closure bullet in `BytecodeRuntimeHints`' javadoc and the
matching part of the `deploymentAot.adoc` bullet ("turning a closure or a map
into an interface, wherever statically compiled code passes or assigns a
closure to one"). Both should say that code compiled with the option converts
its closure literals without a proxy, and what is still registered.
- [ ] Build the native-i18n image and run its checks: `./gradlew
:native-i18n:check -PnativeTests` from `end-to-end`, with a GraalVM `JAVA_HOME`.
## Groovy version
apache/groovy#3026 targets Groovy master (7.0.0-SNAPSHOT). Grails 9 builds
with Groovy 6.0.x, so it only benefits once the change is backported to
`GROOVY_6_0_X`; the PR asks for that. The option is off by default, so the
backport changes nothing until it is set.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]