Skip to content

Commit

Permalink
8336492: Regression in lambda serialization
Browse files Browse the repository at this point in the history
Reviewed-by: vromero
  • Loading branch information
mcimadamore committed Sep 13, 2024
1 parent 358ff19 commit 8a4ea09
Show file tree
Hide file tree
Showing 16 changed files with 837 additions and 1,202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,12 @@ public static EnumSet<Flag> asFlagSet(long flags) {
/**
* Flag that marks a synthetic method body for a lambda expression
*/
public static final long LAMBDA_METHOD = 1L<<49;
public static final long LAMBDA_METHOD = 1L<<49; //MethodSymbols only

/**
* Flag that marks a synthetic local capture field in a local/anon class
*/
public static final long LOCAL_CAPTURE_FIELD = 1L<<49; //VarSymbols only

/**
* Flag to control recursion in TransTypes
Expand Down
61 changes: 14 additions & 47 deletions src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ protected Attr(Context context) {
unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType);
unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType);
recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext);
initBlockType = new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass);
}

/** Switch: reifiable types in instanceof enabled?
Expand Down Expand Up @@ -628,6 +629,7 @@ public void report(DiagnosticPosition pos, JCDiagnostic details) {
final ResultInfo unknownTypeInfo;
final ResultInfo unknownTypeExprInfo;
final ResultInfo recoveryInfo;
final MethodType initBlockType;

Type pt() {
return resultInfo.pt;
Expand Down Expand Up @@ -1421,7 +1423,7 @@ public void visitBlock(JCBlock tree) {
// created BLOCK-method.
Symbol fakeOwner =
new MethodSymbol(tree.flags | BLOCK |
env.info.scope.owner.flags() & STRICTFP, names.empty, null,
env.info.scope.owner.flags() & STRICTFP, names.empty, initBlockType,
env.info.scope.owner);
final Env<AttrContext> localEnv =
env.dup(tree, env.info.dup(env.info.scope.dupUnshared(fakeOwner)));
Expand Down Expand Up @@ -3524,62 +3526,26 @@ void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkCon
}
}

/* Map to hold 'fake' clinit methods. If a lambda is used to initialize a
* static field and that lambda has type annotations, these annotations will
* also be stored at these fake clinit methods.
*
* LambdaToMethod also use fake clinit methods so they can be reused.
* Also as LTM is a phase subsequent to attribution, the methods from
* clinits can be safely removed by LTM to save memory.
*/
private Map<ClassSymbol, MethodSymbol> clinits = new HashMap<>();

public MethodSymbol removeClinit(ClassSymbol sym) {
return clinits.remove(sym);
}

/* This method returns an environment to be used to attribute a lambda
* expression.
*
* The owner of this environment is a method symbol. If the current owner
* is not a method, for example if the lambda is used to initialize
* a field, then if the field is:
*
* - an instance field, we use the first constructor.
* - a static field, we create a fake clinit method.
* is not a method (e.g. if the lambda occurs in a field initializer), then
* a synthetic method symbol owner is created.
*/
public Env<AttrContext> lambdaEnv(JCLambda that, Env<AttrContext> env) {
Env<AttrContext> lambdaEnv;
Symbol owner = env.info.scope.owner;
if (owner.kind == VAR && owner.owner.kind == TYP) {
//field initializer
// If the lambda is nested in a field initializer, we need to create a fake init method.
// Uniqueness of this symbol is not important (as e.g. annotations will be added on the
// init symbol's owner).
ClassSymbol enclClass = owner.enclClass();
Symbol newScopeOwner = env.info.scope.owner;
/* if the field isn't static, then we can get the first constructor
* and use it as the owner of the environment. This is what
* LTM code is doing to look for type annotations so we are fine.
*/
if ((owner.flags() & STATIC) == 0) {
for (Symbol s : enclClass.members_field.getSymbolsByName(names.init)) {
newScopeOwner = s;
break;
}
} else {
/* if the field is static then we need to create a fake clinit
* method, this method can later be reused by LTM.
*/
MethodSymbol clinit = clinits.get(enclClass);
if (clinit == null) {
Type clinitType = new MethodType(List.nil(),
syms.voidType, List.nil(), syms.methodClass);
clinit = new MethodSymbol(STATIC | SYNTHETIC | PRIVATE,
names.clinit, clinitType, enclClass);
clinit.params = List.nil();
clinits.put(enclClass, clinit);
}
newScopeOwner = clinit;
}
lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared(newScopeOwner)));
Name initName = owner.isStatic() ? names.clinit : names.init;
MethodSymbol initSym = new MethodSymbol(BLOCK | (owner.isStatic() ? STATIC : 0) | SYNTHETIC | PRIVATE,
initName, initBlockType, enclClass);
initSym.params = List.nil();
lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dupUnshared(initSym)));
} else {
lambdaEnv = env.dup(that, env.info.dup(env.info.scope.dup()));
}
Expand Down Expand Up @@ -3936,6 +3902,7 @@ private void setFunctionalInfo(final Env<AttrContext> env, final JCFunctionalExp
inferenceContext -> setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType),
inferenceContext.asInstType(primaryTarget), checkContext));
} else {
fExpr.owner = env.info.scope.owner;
if (pt.hasTag(CLASS)) {
fExpr.target = primaryTarget;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package com.sun.tools.javac.comp;

import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.List;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.SequencedSet;
import java.util.Set;

import static com.sun.tools.javac.code.Kinds.Kind.MTH;
import static com.sun.tools.javac.code.Kinds.Kind.VAR;

/**
* A visitor which collects the set of local variables "captured" by a given tree.
*/
public class CaptureScanner extends TreeScanner {

/**
* The tree under analysis.
*/
private final JCTree tree;

/**
* The set of local variable declarations encountered in the tree under analysis.
*/
private final Set<Symbol.VarSymbol> seenVars = new HashSet<>();

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

public CaptureScanner(JCTree ownerTree) {
this.tree = ownerTree;
}

@Override
public void visitIdent(JCTree.JCIdent tree) {
Symbol sym = tree.sym;
if (sym.kind == VAR && sym.owner.kind == MTH) {
Symbol.VarSymbol vsym = (Symbol.VarSymbol) sym;
if (vsym.getConstValue() == null && !seenVars.contains(vsym)) {
addFreeVar(vsym);
}
}
}

/**
* Add free variable to fvs list unless it is already there.
*/
protected void addFreeVar(Symbol.VarSymbol v) {
fvs.add(v);
}

@Override
public void visitVarDef(JCTree.JCVariableDecl tree) {
if (tree.sym.owner.kind == MTH) {
seenVars.add(tree.sym);
}
super.visitVarDef(tree);
}

/**
* Obtains the list of captured local variables in the tree under analysis.
*/
List<Symbol.VarSymbol> analyzeCaptures() {
scan(tree);
return List.from(fvs);
}
}
Loading

1 comment on commit 8a4ea09

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.