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)

Reply via email to