Skip to content

GROOVY-11905: Optimize non-capturing lambdas#2438

Open
daniellansun wants to merge 1 commit intomasterfrom
GROOVY-11905
Open

GROOVY-11905: Optimize non-capturing lambdas#2438
daniellansun wants to merge 1 commit intomasterfrom
GROOVY-11905

Conversation

@daniellansun
Copy link
Copy Markdown
Contributor

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 5, 2026

Codecov Report

❌ Patch coverage is 91.14391% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.5402%. Comparing base (d009dd2) to head (078cdef).

Files with missing lines Patch % Lines
...ovy/classgen/asm/sc/StaticTypesLambdaAnalyzer.java 91.2281% 1 Missing and 14 partials ⚠️
...roovy/classgen/asm/sc/StaticTypesLambdaWriter.java 91.0000% 4 Missing and 5 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #2438        +/-   ##
==================================================
+ Coverage     66.4917%   66.5402%   +0.0485%     
- Complexity      30294      30334        +40     
==================================================
  Files            1412       1413         +1     
  Lines          118108     118315       +207     
  Branches        20999      21054        +55     
==================================================
+ Hits            78532      78727       +195     
  Misses          33095      33095                
- Partials         6481       6493        +12     
Files with missing lines Coverage Δ
...roovy/classgen/asm/sc/StaticTypesLambdaWriter.java 94.4751% <91.0000%> (-2.7662%) ⬇️
...ovy/classgen/asm/sc/StaticTypesLambdaAnalyzer.java 91.2281% <91.2281%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@asf-gitbox-commits asf-gitbox-commits force-pushed the GROOVY-11905 branch 2 times, most recently from 738ec2b to e0e80a6 Compare April 5, 2026 15:50
@daniellansun daniellansun requested a review from blackdrag April 6, 2026 02:49
@asf-gitbox-commits asf-gitbox-commits force-pushed the GROOVY-11905 branch 10 times, most recently from a5d3dfb to bbd31a1 Compare April 11, 2026 14:56
@daniellansun daniellansun requested a review from Copilot April 12, 2026 12:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements GROOVY-11905 by optimizing statically-compiled, non-capturing lambdas to avoid per-invocation allocations by emitting a static doCall target and using capture-free invokedynamic/LambdaMetafactory bootstraps, with extensive new bytecode and behavioral tests.

Changes:

  • Add a lambda analysis pass to detect instance-member capture and qualify outer static member references to remain capture-free.
  • Update static-compilation lambda bytecode generation to use H_INVOKESTATIC/no captured receiver when safe, and adjust serializable-lambda deserialization helpers accordingly.
  • Expand test coverage for non-capturing vs capturing lambdas (behavioral + bytecode assertions) and update an existing type-annotation expectation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/test/groovy/org/codehaus/groovy/classgen/asm/TypeAnnotationsTest.groovy Updates expected lambda doCall signature to match new static non-capturing implementation.
src/test/groovy/groovy/transform/stc/LambdaTest.groovy Adds a large nested test suite covering non-capturing/capturing semantics and emitted bytecode patterns.
src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaWriter.java Implements capture-free invokedynamic generation for non-capturing lambdas and refactors serialize/deserialize support.
src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesLambdaAnalyzer.java New analyzer to detect capture and qualify outer static member references for static doCall.
src/main/java/org/codehaus/groovy/ast/ClassNode.java Caches getOuterClasses() result.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1455 to +1469
private List<ClassNode> outerClasses;
public List<ClassNode> getOuterClasses() {
List<ClassNode> ocs = outerClasses;
if (ocs != null) return ocs;

ClassNode outer = getOuterClass();
if (outer == null) {
return Collections.emptyList();
return outerClasses = Collections.emptyList();
}
List<ClassNode> result = new ArrayList<>(4);
do {
result.add(outer);
} while ((outer = outer.getOuterClass()) != null);

return result;
return outerClasses = Collections.unmodifiableList(result);
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOuterClasses() now caches the computed list in a field, but the implementation doesn’t account for redirect/proxy ClassNodes. Since setRedirect(...) can be called later for non-primary nodes, a proxy may cache Collections.emptyList() (or an outdated chain) before redirect is set/changed and then return stale results thereafter. Consider delegating to redirect().getOuterClasses() when redirect != null and/or clearing outerClasses in setRedirect(...) to keep the cache correct.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants