This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/groovy-dev-site.git
The following commit(s) were added to refs/heads/asf-site by this push:
new c3957e67 2026/07/12 14:16:05: Generated dev website from
groovy-website@436de33
c3957e67 is described below
commit c3957e67fe77ab4c1ed907bffdb16956df7d2e5c
Author: jenkins <[email protected]>
AuthorDate: Sun Jul 12 14:16:05 2026 +0000
2026/07/12 14:16:05: Generated dev website from groovy-website@436de33
---
search/search-index.json | 2 +-
wiki/GEP-27.html | 52 ++++++++++++++++++++++++++++++++++--------------
2 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/search/search-index.json b/search/search-index.json
index ff39e163..66c64395 100644
--- a/search/search-index.json
+++ b/search/search-index.json
@@ -891,7 +891,7 @@
{
"id": "wiki/GEP-27.html",
"title": "The Apache Groovy programming language - Developer docs -
GEP-27",
- "content": "The Apache Groovy programming language - Developer docs -
GEP-27 Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky
Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on
GitHub Report issues in Jira Stack Overflow questions Slack Community You are
using an outdated browser. Please upgrade your browser to improve your
experience. Apache Groovy™ Learn Documentation Download Support
Contribute Ecosystem Blog posts Socialize GE [...]
+ "content": "The Apache Groovy programming language - Developer docs -
GEP-27 Socialize Discuss on the mailing list Groovy on X Groovy on Bluesky
Groovy on Mastodon Groovy on LinkedIn Events and conferences Source code on
GitHub Report issues in Jira Stack Overflow questions Slack Community You are
using an outdated browser. Please upgrade your browser to improve your
experience. Apache Groovy™ Learn Documentation Download Support
Contribute Ecosystem Blog posts Socialize GE [...]
"url": "wiki/GEP-27.html",
"site": "dev"
},
diff --git a/wiki/GEP-27.html b/wiki/GEP-27.html
index aefff7e2..710147d1 100644
--- a/wiki/GEP-27.html
+++ b/wiki/GEP-27.html
@@ -222,8 +222,9 @@ per-closure class, while remaining a real
<code>groovy.lang.Closure</code> (so <
<div class="paragraph">
<p>The proposal is deliberately phased. A <strong>small, sound-by-construction
slice — eliminating the
generated class for non-capturing and read-only-capturing SAM lambdas — is
merged for Groovy 6.0</strong>.
-<strong>Closure packing (S1), including its soundness analysis and a
typed-dispatch performance path, is
-now implemented and proposed as a Groovy 6.0 stretch goal
(GROOVY-12151)</strong>; the remaining, more
+<strong>Closure packing (S1), including its soundness analysis and a
<code>MethodHandle</code>-based dispatch path that
+reaches <code>@CompileStatic</code> call-parity, is now implemented and
proposed as a Groovy 6.0 stretch goal
+(GROOVY-12151)</strong>; the remaining, more
aggressive work — object elision and unifying the two writers — targets Groovy
7.0. The change is
strictly additive, defaults can be flipped via a flag for back-out, and it is
gated so that any
tooling that reflects on generated-class names has an escape hatch.</p>
@@ -792,9 +793,14 @@ to S0 (a normal closure class) otherwise; under dynamic
compilation it is opt-in
<code>@PackedClosures</code>, backed by the runtime guard.</p>
</li>
<li>
-<p><strong>Typed static dispatch</strong> — the hoisted body is typed from the
inferred types the type checker
-already computed and compiles statically (typed captures/params/return, zero
<code>invokedynamic</code> in
-the body), so packing under <code>@CompileStatic</code> is a speed win and not
merely a class-count win.</p>
+<p><strong>Fast dispatch</strong> — two parts. The hoisted body is typed from
the inferred types the type checker
+already computed and compiles statically (typed captures/params/return, zero
<code>invokedynamic</code> in the
+body). And the shared adapter reaches that body through a
<code>MethodHandle</code> constant to the private
+hoisted method, invoked with <code>invokeExact</code> (no reflection), behind
a <code>doCall</code> fast path that skips
+argument adaptation on the common exact-arity call. Measured per
<code>call()</code> against the generated
+closure class it replaces: a naive shared adapter is ~7–9× slower, whereas the
shipped one reaches
+<code>@CompileStatic</code> parity and ~2× under dynamic compilation — so
packing is a class-count win at no
+dispatch-speed penalty under <code>@CompileStatic</code>.</p>
</li>
</ul>
</div>
@@ -1093,10 +1099,14 @@ a scope out entirely. The automatic flag path stays
lenient.</p>
</div>
<div class="paragraph">
<p>Also implemented and validated: the <strong>capability analysis</strong>
(STC’s delegate-independence proof driving
-automatic packing under <code>@CompileStatic</code>, no annotation needed) and
<strong>typed static dispatch</strong>
-(statically-compiled hoisted bodies with typed captures/params/return — zero
<code>invokedynamic</code>), with
-compile-time packing exercised across the core module and several others. This
implemented slice is
-proposed as a Groovy 6.0 stretch goal (GROOVY-12151). The remaining Groovy 7
work is owner-correct
+automatic packing under <code>@CompileStatic</code>, no annotation needed) and
the <strong>dispatch path</strong> — a
+statically-compiled, typed hoisted body reached through a
<code>MethodHandle</code> constant to the private
+hoisted method (<code>invokeExact</code>, no reflection) behind a lean
<code>doCall</code> fast path — exercised across the
+core module and several others. Measured per <code>call()</code> against the
generated closure class it replaces,
+a naive shared adapter is ~7–9× slower; the shipped adapter reaches
<code>@CompileStatic</code> parity and ~2×
+under dynamic compilation. (Marking the adapter <code>GeneratedClosure</code>
for the metaclass fast path was
+evaluated and found perf-neutral once <code>doCall</code> was lean, so it is
not applied.) This implemented slice
+is proposed as a Groovy 6.0 stretch goal (GROOVY-12151). The remaining Groovy
7 work is owner-correct
retargeting so self-contained nested closures can hoist, S3 object elision,
unifying the closure and
lambda writers, and hardening the automatic path from experimental (opt-in
flag) to default-on.</p>
</div>
@@ -1111,6 +1121,17 @@ of a closure class’s bytes were
<code>Closure</code> scaffolding</strong>
are the extreme case: a representative <code>RemarkCriteria</code> domain
generated <strong>24 closure classes
totalling 80 KB</strong> — more bytecode than the entire rest of the class
(1.19×).</p>
</div>
+<div class="paragraph">
+<p>Dispatch speed matters as much as class count, because a shared adapter is
invoked through the
+<code>Closure</code> machinery rather than being its own class. A naive
adapter (reflective invoke plus per-call
+argument adaptation) measured ~7–9× slower to <code>call()</code> than the
generated closure class it replaces —
+smaller but slower. The shipped adapter closes that gap with a compile-time
<code>MethodHandle</code> constant
+invoked via <code>invokeExact</code> and a <code>doCall</code> fast path that
skips adaptation on the common exact-arity
+call: a packed <code>@CompileStatic</code> closure then dispatches at parity
with a normal closure, and a packed
+dynamic closure at ~2×. So <code>@CompileStatic</code> packing is strictly a
win (fewer classes, same call
+speed), while dynamic packing trades ~2× call cost for the class-count
reduction — which is why the
+dynamic path stays opt-in.</p>
+</div>
</div>
<div class="sect2">
<h3 id="_grails_artefact_dsls_and_nested_closures">Grails artefact DSLs and
nested closures</h3>
@@ -1177,8 +1198,8 @@ suite before it lands.</p>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">3</p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">Closure
packing (S1) + capability/escape analysis + typed static dispatch +
<code>@PackedClosures</code></p></td>
-<td class="tableblock halign-left valign-top"><p class="tableblock">Sound
closure packing: automatic and statically dispatched under
<code>@CompileStatic</code>, opt-in (with runtime guard + escape decline) under
dynamic</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Closure
packing (S1) + capability/escape analysis + <code>MethodHandle</code> dispatch
+ <code>@PackedClosures</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Sound
closure packing: automatic under <code>@CompileStatic</code> at call-parity
dispatch, opt-in (with runtime guard + escape decline) under dynamic</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Groovy 6.0
(stretch, GROOVY-12151)</p></td>
</tr>
<tr>
@@ -1299,10 +1320,11 @@ the compatibility valve while tools are updated.</p>
is dead weight, not an allocation).</p>
</li>
<li>
-<p>Closure packing under <code>@CompileStatic</code> is both a class-count win
and a speed win: the hoisted
-body is typed from the checker’s inferred types and compiles statically
(zero <code>invokedynamic</code> in
-the body), implemented in the S1 slice — so a packed
<code>@CompileStatic</code> closure is smaller without
-trading its body’s static compilation for dynamic dispatch.</p>
+<p>Closure packing under <code>@CompileStatic</code> is a class-count win at
dispatch parity with a normal
+closure: the typed hoisted body compiles statically, and the adapter reaches
it through a
+<code>MethodHandle</code> constant (<code>invokeExact</code>) behind a lean
<code>doCall</code> fast path. Measured per <code>call()</code>, a
+naive shared adapter is ~7–9× slower; the shipped one is at parity under
<code>@CompileStatic</code> and ~2×
+under dynamic compilation (so the dynamic path stays opt-in).</p>
</li>
</ul>
</div>