RFR: 8336492: Regression in lambda serialization [v16]

ExE Boss duke at openjdk.org
Wed Aug 7 20:47:35 UTC 2024


On Thu, 1 Aug 2024 13:02:02 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

>> This PR consolidates the code for dealing with local captures in both `Lower` and `LambdaToMethod`. It does so by adding a new tree scanner, namely `CaptureScanner`, which is then subclassed by `Lower.FreeVarCollector` and also by the new `LambdaToMethod.LambdaCaptureScanner`.
>> 
>> The main idea behind the new visitor is that we can compute the set of (most) captured locals w/o relying on complex state from `Lower`, and make the logic general enough to work on *any* tree. This can be done by keeping track of all local variable declarations occurring in a given subtree `T`. Then, if `T` contains a reference to a local variable that has not been seen, we can assume that this variable is a captured variable. This simple logic works rather well, and doesn't rely on checking e.g. that the accessed variable has the same owner of the method that owns a local class (which then caused other artifacts such as `Lower::ownerToCopyFreeVarsFrom`, now removed).
>> 
>> The bigger rewrite is the one in `LambdaToMethod`. That class featured a custom visitor called `LambdaAnalyzerPreprocessor` (now removed) which maintains a stack of frames encountered during a visit, and tries to establish which references to local variables needs to be captured by the lambda, as well as whether `this` needs to be referenced by the lambda. Thanks to the new `CaptureScanner` visitor, all this logic can be achieved in a very small subclass of `CaptureScanner`, namely `LambdaCaptureScanner`.
>> 
>> This removes the need to keep track of frames, and other ancillary data structures. `LambdaTranslationContext` is now significantly simpler, and its most important field is a `Map<VarSymbol, VarSymbol>` which maps logical lambda symbols to translated ones (in the generated lambda method). We no longer need to maintain different maps for different kinds of captured symbols.
>> 
>> The code for patching identifiers in a lambda expression also became a lot simpler: we just check if we have pending lambda translation context and, if so, we ask if the identifier symbol is contained in the translation map. Otherwise, we leave the identifier as is.
>> 
>> Fixing the issue referenced by this PR is now very simple: inside `LambdaCaptureScanner`, we just need to make sure that any identifier referring to a captured local field generated by `Lower` is re-captured, and the rest just works. To make the code more robust I've added a new variable flag to denote synthetic captured fields generated by `Lower`.
>> 
>> #### C...
>
> Maurizio Cimadamore has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Fix comment

Using `SequencedSet` would allow to skip the call to `com.sun.tools.javac.util.List​::reverse()` on the result of `CaptureScanner​::analyzeCaptures()`.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java line 36:

> 34: import java.util.HashSet;
> 35: import java.util.LinkedHashSet;
> 36: import java.util.Set;

Suggestion:

import java.util.Set;
import java.util.SequencedSet;

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java line 59:

> 57:      * The set of captured local variables accessed from within the tree under analysis.
> 58:      */
> 59:     private final Set<VarSymbol> fvs = new LinkedHashSet<>();

Suggestion:

    private final SequencedSet<VarSymbol> fvs = new LinkedHashSet<>();

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/CaptureScanner.java line 96:

> 94:     List<Symbol.VarSymbol> analyzeCaptures() {
> 95:         scan(tree);
> 96:         return List.from(fvs);

Suggestion:

        return List.from(fvs.reversed());

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java line 317:

> 315:         }
> 316:         FreeVarCollector collector = new FreeVarCollector(classDef(c));
> 317:         fvs = collector.analyzeCaptures().reverse();

Suggestion:

        fvs = collector.analyzeCaptures();

-------------

PR Review: https://git.openjdk.org/jdk/pull/20349#pullrequestreview-2226139559
PR Review Comment: https://git.openjdk.org/jdk/pull/20349#discussion_r1707861562
PR Review Comment: https://git.openjdk.org/jdk/pull/20349#discussion_r1707854283
PR Review Comment: https://git.openjdk.org/jdk/pull/20349#discussion_r1707860442
PR Review Comment: https://git.openjdk.org/jdk/pull/20349#discussion_r1707860631


More information about the compiler-dev mailing list