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. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
