[
https://issues.apache.org/jira/browse/GROOVY-12365?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King updated GROOVY-12365:
-------------------------------
Description:
Groovy 6 dynamic code runs in a GraalVM native image thanks to the AOT link
mode (GROOVY-12234), but building the image still requires every user to run
the {{native-image-agent}} first, purely to capture the reflection and resource
access Groovy's *own* runtime performs. The Iris example README says it
plainly: without the recorded metadata the image builds and then fails
initialising {{GroovySystem}}. Logging frameworks and most libraries solved
this years ago by shipping their reachability metadata inside the jar
(log4j-core carries
{{META-INF/native-image/org.apache.logging.log4j/log4j-core/reflect-config.json}}
and {{resource-config.json}}); {{native-image}} picks such files up
automatically from the class path, and the GraalVM Gradle/Maven plugins do too.
Groovy ships nothing, and the central [GraalVM reachability-metadata
repository|https://github.com/oracle/graalvm-reachability-metadata] has no
entry for {{org.apache.groovy:groovy}} either (its {{groovy-all}} entry is
marked not-for-native-image and points at a {{groovy}} entry that does not
exist), so plugin users get nothing there as well.
h3. What the agent records for Groovy itself
Two unrelated dynamic applications (the Iris deep-learning example and a small
logging probe, both on GraalVM CE 25.2.4) produce almost the same Groovy-owned
set: about 180 reflection entries and a handful of resources.
* Runtime bootstrap types under {{groovy.lang}},
{{org.codehaus.groovy.runtime}}, {{org.codehaus.groovy.reflection}},
{{org.codehaus.groovy.vmplugin}}: {{GroovySystem}}, {{MetaClassRegistryImpl}},
{{Closure}}, {{GString}}, {{GroovyObjectSupport}}, {{ExpandoMetaClass}},
ranges, {{NullObject}}, the VM plugin, etc.
* The {{Introspector}} probes the metaclass performs for every class it
introspects: {{<Type>BeanInfo}} and {{<Type>Customizer}} lookups for each
Groovy runtime type above (these are lookups of classes that do not exist; they
still need to be registered so the lookup fails quietly rather than with
{{MissingReflectionRegistrationError}}).
* {{org.codehaus.groovy.runtime.dgm$N}} proxy classes (30 to 32 per
application), loaded by name from {{GeneratedMetaMethod$Proxy.createProxy}}
when a DGM method is first selected. This set is application dependent: a DGM
method not exercised during the agent run fails at run time with
{{ClassNotFoundException: org.codehaus.groovy.runtime.dgm$1175}} (seen when a
code path was added after the agent run).
* Resources: {{META-INF/dgminfo}},
{{META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule}},
{{META-INF/services/org.codehaus.groovy.runtime.ExtensionModule}}, plus a
{{.class}} resource lookup ({{groovy/concurrent/AsyncScope.class}}).
h3. Proposal
Ship the Groovy-owned part of that metadata in the {{groovy}} jar under
{{META-INF/native-image/org.apache.groovy/groovy/}} (and the equivalent for
modules that need it, e.g. {{groovy-json}}, {{groovy-xml}}), following the
log4j-core layout, so that a dynamic Groovy application builds with plain
{{native-image -cp ...}} and no agent step:
* {{reflect-config.json}} / {{reachability-metadata.json}} with the bootstrap
types, the {{BeanInfo}} / {{Customizer}} probe names, and the DGM proxies. For
the proxies the safe choice is to register all of them (about 2,000 small
classes, all of which are in the jar anyway) rather than a sample, since the
used set cannot be known ahead of time; alternatively
{{GeneratedMetaMethod$Proxy}} could stop resolving them by name (e.g. a
generated switch or a {{MethodHandle}} table), which would remove the entries
entirely.
* {{resource-config.json}} for {{META-INF/dgminfo}}, the extension-module
descriptors and the service files.
* A {{native-image.properties}} with {{Args}} carrying what the README
currently asks users to type by hand and what the AOT link mode assumes:
{{--initialize-at-run-time=org.codehaus.groovy.vmplugin.v8.IndyInterface}} (and
the other runtime classes that must not be initialised at build time). This
file is also the one place that can set a build-time system property for
everybody, which matters for the JUL caller-location workaround documented
under GROOVY-12354 ({{-Djdk.logger.packages=...}} is captured by a build-time
static initialiser and ignored at run time).
The metadata should be generated by the build rather than hand-maintained: run
the agent over the existing native-image parity corpus (or a small dedicated
script) as part of the performance/native tests, diff against the checked-in
files, and fail on drift. Once the jar ships it, an entry in the
reachability-metadata repository becomes unnecessary (the repository prefers
jar-embedded metadata), but its dangling {{groovy-all}} replacement should be
corrected or an {{org.apache.groovy:groovy}} entry added pointing at the
shipped files.
h3. Out of scope
Caller-location accuracy in native images (GraalVM's visible
MethodHandle-interpreter frames) is tracked under GROOVY-12354's documentation
and the GraalVM issue; the fatal dynamic call to a {{@CallerSensitive}} method
and the failing MOP {{super}} call in native images have their own issues.
> native: Ship Groovy's own reachability metadata in the groovy jar
> -----------------------------------------------------------------
>
> Key: GROOVY-12365
> URL: https://issues.apache.org/jira/browse/GROOVY-12365
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Priority: Major
>
> Groovy 6 dynamic code runs in a GraalVM native image thanks to the AOT link
> mode (GROOVY-12234), but building the image still requires every user to run
> the {{native-image-agent}} first, purely to capture the reflection and
> resource access Groovy's *own* runtime performs. The Iris example README says
> it plainly: without the recorded metadata the image builds and then fails
> initialising {{GroovySystem}}. Logging frameworks and most libraries solved
> this years ago by shipping their reachability metadata inside the jar
> (log4j-core carries
> {{META-INF/native-image/org.apache.logging.log4j/log4j-core/reflect-config.json}}
> and {{resource-config.json}}); {{native-image}} picks such files up
> automatically from the class path, and the GraalVM Gradle/Maven plugins do
> too. Groovy ships nothing, and the central [GraalVM reachability-metadata
> repository|https://github.com/oracle/graalvm-reachability-metadata] has no
> entry for {{org.apache.groovy:groovy}} either (its {{groovy-all}} entry is
> marked not-for-native-image and points at a {{groovy}} entry that does not
> exist), so plugin users get nothing there as well.
> h3. What the agent records for Groovy itself
> Two unrelated dynamic applications (the Iris deep-learning example and a
> small logging probe, both on GraalVM CE 25.2.4) produce almost the same
> Groovy-owned set: about 180 reflection entries and a handful of resources.
> * Runtime bootstrap types under {{groovy.lang}},
> {{org.codehaus.groovy.runtime}}, {{org.codehaus.groovy.reflection}},
> {{org.codehaus.groovy.vmplugin}}: {{GroovySystem}},
> {{MetaClassRegistryImpl}}, {{Closure}}, {{GString}}, {{GroovyObjectSupport}},
> {{ExpandoMetaClass}}, ranges, {{NullObject}}, the VM plugin, etc.
> * The {{Introspector}} probes the metaclass performs for every class it
> introspects: {{<Type>BeanInfo}} and {{<Type>Customizer}} lookups for each
> Groovy runtime type above (these are lookups of classes that do not exist;
> they still need to be registered so the lookup fails quietly rather than with
> {{MissingReflectionRegistrationError}}).
> * {{org.codehaus.groovy.runtime.dgm$N}} proxy classes (30 to 32 per
> application), loaded by name from {{GeneratedMetaMethod$Proxy.createProxy}}
> when a DGM method is first selected. This set is application dependent: a DGM
> method not exercised during the agent run fails at run time with
> {{ClassNotFoundException: org.codehaus.groovy.runtime.dgm$1175}} (seen when a
> code path was added after the agent run).
> * Resources: {{META-INF/dgminfo}},
> {{META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule}},
> {{META-INF/services/org.codehaus.groovy.runtime.ExtensionModule}}, plus a
> {{.class}} resource lookup ({{groovy/concurrent/AsyncScope.class}}).
> h3. Proposal
> Ship the Groovy-owned part of that metadata in the {{groovy}} jar under
> {{META-INF/native-image/org.apache.groovy/groovy/}} (and the equivalent for
> modules that need it, e.g. {{groovy-json}}, {{groovy-xml}}), following the
> log4j-core layout, so that a dynamic Groovy application builds with plain
> {{native-image -cp ...}} and no agent step:
> * {{reflect-config.json}} / {{reachability-metadata.json}} with the bootstrap
> types, the {{BeanInfo}} / {{Customizer}} probe names, and the DGM proxies.
> For the proxies the safe choice is to register all of them (about 2,000 small
> classes, all of which are in the jar anyway) rather than a sample, since the
> used set cannot be known ahead of time; alternatively
> {{GeneratedMetaMethod$Proxy}} could stop resolving them by name (e.g. a
> generated switch or a {{MethodHandle}} table), which would remove the entries
> entirely.
> * {{resource-config.json}} for {{META-INF/dgminfo}}, the extension-module
> descriptors and the service files.
> * A {{native-image.properties}} with {{Args}} carrying what the README
> currently asks users to type by hand and what the AOT link mode assumes:
> {{--initialize-at-run-time=org.codehaus.groovy.vmplugin.v8.IndyInterface}}
> (and the other runtime classes that must not be initialised at build time).
> This file is also the one place that can set a build-time system property for
> everybody, which matters for the JUL caller-location workaround documented
> under GROOVY-12354 ({{-Djdk.logger.packages=...}} is captured by a build-time
> static initialiser and ignored at run time).
> The metadata should be generated by the build rather than hand-maintained:
> run the agent over the existing native-image parity corpus (or a small
> dedicated script) as part of the performance/native tests, diff against the
> checked-in files, and fail on drift. Once the jar ships it, an entry in the
> reachability-metadata repository becomes unnecessary (the repository prefers
> jar-embedded metadata), but its dangling {{groovy-all}} replacement should be
> corrected or an {{org.apache.groovy:groovy}} entry added pointing at the
> shipped files.
> h3. Out of scope
> Caller-location accuracy in native images (GraalVM's visible
> MethodHandle-interpreter frames) is tracked under GROOVY-12354's
> documentation and the GraalVM issue; the fatal dynamic call to a
> {{@CallerSensitive}} method and the failing MOP {{super}} call in native
> images have their own issues.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)