[ 
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)

Reply via email to