[
https://issues.apache.org/jira/browse/GROOVY-12143?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18095117#comment-18095117
]
ASF GitHub Bot commented on GROOVY-12143:
-----------------------------------------
paulk-asert opened a new pull request, #2680:
URL: https://github.com/apache/groovy/pull/2680
A statically compiled lambda targeting a functional interface currently
generates a per-lambda class extending groovy.lang.Closure, even though its
runtime value is a LambdaMetafactory-produced functional-interface instance.
For a capturing lambda the class also holds the captured state
(Reference-wrapped) and is instantiated per lambda.
Like the JVM does for Java lambdas, hoist the lambda body onto the enclosing
class as a private static method and bootstrap LambdaMetafactory directly
against it, so no per-lambda class is generated:
- Non-capturing: re-home the already-static doCall onto the enclosing class;
the metafactory produces the same zero-allocation singleton.
- Read-only capturing: emit a static method taking the captured values as
leading parameters and have the metafactory capture those values directly -
eliminating the lambda class, its instance, and the Reference wrapping.
Captured Reference-field reads in the doCall are repointed to the value
parameters (codegen then loads the value directly).
Only lambdas sitting directly in a real method are hoisted. Serializable,
nested, instance- accessing, and mutated-capture lambdas (which need the shared
Reference) are unchanged and keep their generated class. Behaviour and metadata
are preserved (verified: Predicate/ BiFunction, multi-capture, mixed
capture/SAM types, lambdas in static methods, qualified outer static calls,
serializable round-trips, singleton identity for non-capturing, mutated-
capture mutation across invocations, parameter type annotations on the hoisted
method); stack traces now name a $lambda$ method on the enclosing class
(Java-like).
Gated by an opt-in system property, groovy.target.lambda.hoist (default
off), read per lambda so it can be toggled without a JVM restart, while
IDE/tooling compatibility with the changed generated-class shape is verified.
Default off keeps existing behaviour unchanged.
Testing:
- LambdaHoistTest covers the opt-in path (non-capturing and
read-only-capturing hoist, singleton identity, multi-capture behaviour) and the
fall-back cases (mutated capture, instance-access, serializable, nested,
default-off).
- Existing tests that assert the pre-hoist generated-class structure
(LambdaTest.NonCapturingLambdaOptimizationTest,
LambdaTest.NativeLambdaBytecodeTest, LambdaTest.testLambdaClassIsntSynthetic,
TypeAnnotationsTest.testTypeAnnotationsForLambda) are annotated
@DisabledIfSystemProperty so they skip when the flag is on; the build forwards
-P/-Dgroovy.target.lambda.hoist to the test JVM.
- The full core test suite passes with the flag on (0 failures; only the
pre-hoist bytecode-shape tests skip) and is unchanged with it off.
See GEP-27 (Compact Closure and Lambda Compilation) for the broader design.
> GEP-27: lambda hoisting
> ------------------------
>
> Key: GROOVY-12143
> URL: https://issues.apache.org/jira/browse/GROOVY-12143
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)