Daniel Sun created GROOVY-12185:
-----------------------------------
Summary: Move classic call-site caching into optional
groovy-callsite module
Key: GROOVY-12185
URL: https://issues.apache.org/jira/browse/GROOVY-12185
Project: Groovy
Issue Type: Improvement
Reporter: Daniel Sun
h2. Background
Since Groovy 4, invokedynamic is the default call-dispatch path. The classic
(pre-indy) call-site cache under {{org.codehaus.groovy.runtime.callsite}} still
lived in groovy-core, so every runtime paid for types that default compilation
no longer needs.
The *primary purpose* of extracting this code is *runtime binary compatibility*
for classes compiled by *Groovy 4 and Groovy 5* (and earlier releases that
always used classic call sites). Those classes embed {{$getCallSiteArray}} /
{{CallSiteArray}} / {{CallSite}} linkage. On Groovy 6 they must still *load and
execute* when {{org.apache.groovy:groovy-callsite}} is on the classpath.
Secondary use: compiling with {{indy}} disabled
({{{}groovy.target.indy=false{}}} or optimization option {{{}indy: false{}}}).
This implements the direction from the PR #1934 / GROOVY-11158: isolate classic
call-site caching into an optional, deprecated module.
h2. Goal
* *Compatibility first:* preserve the public linkage surface of {{CallSite}}
and {{CallSiteArray}} so Groovy 4/5 classic bytecode runs on Groovy 6 +
{{{}groovy-callsite{}}}.
* Move classic call-site runtime into optional module
{{org.apache.groovy:groovy-callsite}} (not part of {{{}groovy-all{}}}).
* Keep groovy-core free of a dependency on that module; default path stays
invokedynamic-only.
* Expose enough public MetaClass selection API so the optional module can
build sites without package-private coupling into core.
* When {{indy}} is off and the classic runtime is missing, *fail at compile
time* with a clear error instead of emitting unloadable bytecode.
h2. Design
h3. Module boundary
* New subproject: {{subprojects/groovy-callsite}}
* Depends on groovy-core ({{{}api{}}}); core never depends on
{{groovy-callsite}}
* {{optionalModule()}} — not shipped in {{groovy-all}}
* Entire package {{org.codehaus.groovy.runtime.callsite}} is {{@Deprecated}}
(removal planned in a later major)
h3. What moves where
||Former location (core)||New location ({{{}groovy-callsite{}}})||
|{{MetaClassImpl}} / {{ExpandoMetaClass}} / {{ClosureMetaClass}}
{{create…CallSite}} factories|{{MetaClassCallSites}}|
|{{CachedMethod}} {{create…MetaMethodSite}} (+ SoftReference compile
cache)|{{CachedMethodCallSites}} (side map of per-method state)|
|{{CallSiteArray}} hooks into those MetaClass factories|{{CallSiteArray}} →
{{MetaClassCallSites}}|
|{{{}CallSite{}}}, {{{}AbstractCallSite{}}}, specialized sites,
{{{}CallSiteGenerator{}}}, …|same package names under the module|
h3. Core API kept for optional consumers (since 6.0.0)
* {{MetaClassImpl#getMethodWithCaching(Class, String, Class[])}}
* {{MetaClassImpl#chooseConstructor(Class[])}}
* {{MetaClassImpl#chooseNamedArgCompatibleConstructor(Class[], Object[])}}
* {{ExpandoMetaClass#hasCustomInvokeMethod()}}
h3. Core clean-up (major-version break)
Removed from groovy-core:
* {{create…CallSite}} methods on {{MetaClassImpl}} / {{ExpandoMetaClass}} /
{{ClosureMetaClass}}
* {{CachedMethod}} {{create…MetaMethodSite}} and related SoftReference fields
* {{CachedClass#getCallSiteLoader()}}
* {{CallSiteAwareMetaMethod}} and DGM/array call-site specializations
Retained in core:
* DGM {{invoke()}} paths (numbers, arrays) for MOP / indy
* Helpers under {{org.codehaus.groovy.runtime}} (e.g.
{{{}BooleanClosureWrapper{}}})
* Classic *bytecode emission* ({{{}CallSiteWriter{}}}) only when {{indy}} is
disabled
h3. Compiler guard
When {{indy}} is off, {{WriterController}} requires {{CallSiteArray}} on the
compilation class loader before emitting classic call-site bytecode. Missing
module → clear error pointing at {{groovy-callsite}} / GROOVY-11158.
h2. Migration
h4. Default users (indy on — default since Groovy 4)
No action. Core jar no longer ships classic call-site classes.
h4. Run classes compiled by Groovy 4/5 classic mode (or older)
Add the optional module to the *runtime* classpath:
{code:xml}
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-callsite</artifactId>
<version>${groovy.version}</version>
</dependency>
{code}
{code:groovy}
// Gradle
runtimeOnly "org.apache.groovy:groovy-callsite:${groovyVersion}"
{code}
Use the published (jarjar) artifact together with groovy-core so shaded ASM
({{{}groovyjarjarasm{}}}) resolves for {{{}CallSiteGenerator{}}}.
h4. Compile with indy disabled
Same module on the *compile* classpath. Without it, compilation fails fast
rather than producing unloadable classes.
h4. Code that called removed core factory APIs
* Prefer invokedynamic (default), or
* Depend on {{groovy-callsite}} and use {{MetaClassCallSites}} /
{{CachedMethodCallSites}} instead of removed {{MetaClassImpl}} /
{{CachedMethod}} factories.
h2. Compatibility contract
Stable for precompiled classic bytecode:
* Package: {{org.codehaus.groovy.runtime.callsite}}
* {{{}CallSiteArray(Class, String[]){}}}, public fields {{array}} /
{{{}owner{}}}, {{NOPARAM}}
* Full {{CallSite}} method set ({{{}call{}}}, {{{}callCurrent{}}},
{{{}callStatic{}}}, {{{}callConstructor{}}}, {{{}callGetProperty{}}},
{{{}callSafe{}}}, …)
Binary break in core is intentional for a major version. japicmp against 5.x is
disabled for the new module ({{{}withoutBinaryCompatibilityChecks(){}}}).
--
This message was sent by Atlassian Jira
(v8.20.10#820010)