8336492: Regression in lambda serialization

Reviewed-by: vromero
This commit is contained in:
Maurizio Cimadamore 2024-09-13 12:04:31 +00:00
parent 358ff19633
commit 8a4ea09fa2
16 changed files with 895 additions and 1260 deletions

View File

@ -285,7 +285,12 @@ public class 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

View File

@ -182,6 +182,7 @@ public class Attr extends JCTree.Visitor {
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?
@ -628,6 +629,7 @@ public class Attr extends JCTree.Visitor {
final ResultInfo unknownTypeInfo;
final ResultInfo unknownTypeExprInfo;
final ResultInfo recoveryInfo;
final MethodType initBlockType;
Type pt() {
return resultInfo.pt;
@ -1421,7 +1423,7 @@ public class Attr extends JCTree.Visitor {
// 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)));
@ -3524,62 +3526,26 @@ public class Attr extends JCTree.Visitor {
}
}
/* 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()));
}
@ -3936,6 +3902,7 @@ public class Attr extends JCTree.Visitor {
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;
}

View File

@ -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);
}
}

View File

@ -267,26 +267,22 @@ public class Lower extends TreeTranslator {
Map<ClassSymbol,List<VarSymbol>> freevarCache;
/** A navigator class for collecting the free variables accessed
* from a local class. There is only one case; all other cases simply
* traverse down the tree. This class doesn't deal with the specific
* of Lower - it's an abstract visitor that is meant to be reused in
* order to share the local variable capture logic.
* from a local class.
*/
abstract class BasicFreeVarCollector extends TreeScanner {
class FreeVarCollector extends CaptureScanner {
/** Add all free variables of class c to fvs list
* unless they are already there.
*/
abstract void addFreeVars(ClassSymbol c);
/** If tree refers to a variable in owner of local class, add it to
* free variables list.
*/
public void visitIdent(JCIdent tree) {
visitSymbol(tree.sym);
FreeVarCollector(JCTree ownerTree) {
super(ownerTree);
}
void addFreeVars(ClassSymbol c) {
List<VarSymbol> fvs = freevarCache.get(c);
if (fvs != null) {
for (List<VarSymbol> l = fvs; l.nonEmpty(); l = l.tail) {
addFreeVar(l.head);
}
}
}
// where
abstract void visitSymbol(Symbol _sym);
/** If tree refers to a class instance creation expression
* add all free variables of the freshly created class.
@ -306,84 +302,6 @@ public class Lower extends TreeTranslator {
}
super.visitApply(tree);
}
@Override
public void visitYield(JCYield tree) {
scan(tree.value);
}
}
/**
* Lower-specific subclass of {@code BasicFreeVarCollector}.
*/
class FreeVarCollector extends BasicFreeVarCollector {
/** The owner of the local class.
*/
Symbol owner;
/** The local class.
*/
ClassSymbol clazz;
/** The list of owner's variables accessed from within the local class,
* without any duplicates.
*/
List<VarSymbol> fvs;
FreeVarCollector(ClassSymbol clazz) {
this.clazz = clazz;
this.owner = clazz.owner;
this.fvs = List.nil();
}
/** Add free variable to fvs list unless it is already there.
*/
private void addFreeVar(VarSymbol v) {
for (List<VarSymbol> l = fvs; l.nonEmpty(); l = l.tail)
if (l.head == v) return;
fvs = fvs.prepend(v);
}
@Override
void addFreeVars(ClassSymbol c) {
List<VarSymbol> fvs = freevarCache.get(c);
if (fvs != null) {
for (List<VarSymbol> l = fvs; l.nonEmpty(); l = l.tail) {
addFreeVar(l.head);
}
}
}
@Override
void visitSymbol(Symbol _sym) {
Symbol sym = _sym;
if (sym.kind == VAR || sym.kind == MTH) {
if (sym != null && sym.owner != owner)
sym = proxies.get(sym);
if (sym != null && sym.owner == owner) {
VarSymbol v = (VarSymbol)sym;
if (v.getConstValue() == null) {
addFreeVar(v);
}
}
}
}
}
ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
if (!c.isDirectlyOrIndirectlyLocal()) {
return null;
}
Symbol currentOwner = c.owner;
while (currentOwner.owner.kind.matches(KindSelector.TYP) && currentOwner.isDirectlyOrIndirectlyLocal()) {
currentOwner = currentOwner.owner;
}
if (currentOwner.owner.kind.matches(KindSelector.VAL_MTH) && c.isSubClass(currentOwner, types)) {
return (ClassSymbol)currentOwner;
}
return null;
}
/** Return the variables accessed from within a local class, which
@ -395,22 +313,10 @@ public class Lower extends TreeTranslator {
if (fvs != null) {
return fvs;
}
if (c.owner.kind.matches(KindSelector.VAL_MTH) && !c.isStatic()) {
FreeVarCollector collector = new FreeVarCollector(c);
collector.scan(classDef(c));
fvs = collector.fvs;
freevarCache.put(c, fvs);
return fvs;
} else {
ClassSymbol owner = ownerToCopyFreeVarsFrom(c);
if (owner != null) {
fvs = freevarCache.get(owner);
freevarCache.put(c, fvs);
return fvs;
} else {
return List.nil();
}
}
FreeVarCollector collector = new FreeVarCollector(classDef(c));
fvs = collector.analyzeCaptures().reverse();
freevarCache.put(c, fvs);
return fvs;
}
Map<TypeSymbol,EnumMapping> enumSwitchMap = new LinkedHashMap<>();
@ -1501,7 +1407,7 @@ public class Lower extends TreeTranslator {
* @param owner The class in which the definitions go.
*/
List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner) {
return freevarDefs(pos, freevars, owner, 0);
return freevarDefs(pos, freevars, owner, LOCAL_CAPTURE_FIELD);
}
List<JCVariableDecl> freevarDefs(int pos, List<VarSymbol> freevars, Symbol owner,
@ -1517,7 +1423,12 @@ public class Lower extends TreeTranslator {
proxyName = proxyName(v.name, index++);
} while (!proxyNames.add(proxyName));
VarSymbol proxy = new VarSymbol(
flags, proxyName, v.erasure(types), owner);
flags, proxyName, v.erasure(types), owner) {
@Override
public Symbol baseSymbol() {
return v;
}
};
proxies.put(v, proxy);
JCVariableDecl vd = make.at(pos).VarDef(proxy, null);
vd.vartype = access(vd.vartype);

View File

@ -679,6 +679,7 @@ public class TransTypes extends TreeTranslator {
JCLambda slam = make.Lambda(params.toList(), expr);
slam.target = tree.target;
slam.owner = tree.owner;
slam.type = tree.type;
slam.pos = tree.pos;
slam.wasMethodReference = true;

View File

@ -329,7 +329,7 @@ public class ClassWriter extends ClassFile {
int alenIdx = writeAttr(attributeName);
ClassSymbol enclClass = c.owner.enclClass();
MethodSymbol enclMethod =
(c.owner.type == null // local to init block
((c.owner.flags() & BLOCK) != 0 // local to init block
|| c.owner.kind != MTH) // or member init
? null
: ((MethodSymbol)c.owner).originalEnclosingMethod();

View File

@ -807,6 +807,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** list of target types inferred for this functional expression. */
public Type target;
/** The owner of this functional expression. */
public Symbol owner;
public Type getDescriptorType(Types types) {
return target != null ? types.findDescriptorType(target) : types.createErrorType(null);

View File

@ -273,8 +273,8 @@ public class TreeScanner extends Visitor {
}
public void visitLambda(JCLambda tree) {
scan(tree.body);
scan(tree.params);
scan(tree.body);
}
public void visitParens(JCParens tree) {

View File

@ -74,27 +74,27 @@ public class Scoped {
frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("No scope: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), A).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("A: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), B).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("B: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter"));
frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), C).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("C: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter"));
frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), K).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("K: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
frames = cont.stackWalker(EnumSet.noneOf(StackWalker.Option.class), null).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
System.out.println("null: " + frames);
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$14", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
assertEquals(frames, cont.isDone() ? List.of() : Arrays.asList("yield0", "yield", "lambda$bar$0", "run", "enter0", "enter", "yield0", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "yield0", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
}
assertEquals(res.get(), 2);
}
@ -119,23 +119,23 @@ public class Scoped {
List<String> frames = StackWalker.getInstance().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1"));
assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1"));
frames = StackWalkerHelper.getInstance(C).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter"));
assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter"));
frames = StackWalkerHelper.getInstance(B).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter"));
assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter"));
frames = StackWalkerHelper.getInstance(A).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
assertEquals(frames, Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
assertEquals(frames, Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter"));
frames = StackWalkerHelper.getInstance(K).walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList()));
assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$14", "run", "enter0", "enter", "run", "bar", "lambda$foo$8", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1"));
assertEquals(frames.subList(0, 18), Arrays.asList("lambda$bar$0", "run", "enter0", "enter", "run", "bar", "lambda$foo$0", "run", "enter0", "enter", "run", "foo", "lambda$test1$0", "run", "enter0", "enter", "run", "test1"));
long r = b+1;
});

View File

@ -1,7 +1,7 @@
class LambdaTest --
LambdaTest.<init>()
LambdaTest.foo(i)
LambdaTest.lambda$static$1(arg0)/*synthetic*/
LambdaTest.lambda$static$0(arg0, arg1)/*synthetic*/
LambdaTest.lambda$static$0(arg0)/*synthetic*/
LambdaTest.lambda$static$1(arg0, arg1)/*synthetic*/
static interface LambdaTest$I -- inner
LambdaTest$I.m(x)

View File

@ -1,7 +1,7 @@
class LocalClassTest$1 -- anon
LocalClassTest$1.<init>(final this$0/*implicit*/, final j, final val$i/*synthetic*/)
class LocalClassTest$1CapturingLocal$1 -- anon
LocalClassTest$1CapturingLocal$1.<init>(final this$0/*implicit*/, final val$val$i/*synthetic*/)
LocalClassTest$1CapturingLocal$1.<init>(final this$0/*implicit*/, final val$i/*synthetic*/)
LocalClassTest$1CapturingLocal$1.test()
class LocalClassTest$1CapturingLocal -- inner
LocalClassTest$1CapturingLocal.<init>(final this$0/*implicit*/, final j, final val$i/*synthetic*/)

View File

@ -136,15 +136,15 @@ public class WrongLNTForLambdaTest {
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "lambda$bar$0", simpleLambdaExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaWithVarsExpectedLNT);
"Foo.class").toUri()), "lambda$variablesInLambdas$0", lambdaWithVarsExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo$1FooBar.class").toUri()), "run", insideLambdaWithVarsExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaVoid2VoidExpectedLNT);
"Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaVoid2VoidExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "$deserializeLambda$", deserializeExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "lambda$variablesInLambdas$3", lambdaBridgeExpectedLNT);
"Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaBridgeExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
"Foo.class").toUri()), "assignLambda", assignmentExpectedLNT);
checkClassFile(new File(Paths.get(System.getProperty("user.dir"),

View File

@ -245,7 +245,6 @@ public class EnclosingMethodTest extends TestResult {
// anonymous and local classes in lambda
@ExpectedEnclosingMethod(
info = "EnclosingLambda in EnclosingMethodTest",
enclosingMethod = "<init>",
enclosingClazz = EnclosingMethodTest.class
)
class EnclosingLambda {
@ -325,7 +324,6 @@ public class EnclosingMethodTest extends TestResult {
// anonymous and local classes in lambda
@ExpectedEnclosingMethod(
info = "EnclosingLambda in notEnclosing01",
enclosingMethod = "<init>",
enclosingClazz = notEnclosing01.class
)
class EnclosingLambda {
@ -382,7 +380,6 @@ public class EnclosingMethodTest extends TestResult {
// anonymous and local classes in lambda
@ExpectedEnclosingMethod(
info = "EnclosingLambda in notEnclosing02",
enclosingMethod = "<clinit>",
enclosingClazz = notEnclosing02.class
)
class EnclosingLambda {
@ -460,7 +457,6 @@ public class EnclosingMethodTest extends TestResult {
// anonymous and local classes in lambda
@ExpectedEnclosingMethod(
info = "EnclosingLambda in notEnclosing03",
enclosingMethod = "<init>",
enclosingClazz = notEnclosing03.class
)
class EnclosingLambda {
@ -517,7 +513,6 @@ public class EnclosingMethodTest extends TestResult {
// anonymous and local classes in lambda
@ExpectedEnclosingMethod(
info = "EnclosingLambda in notEnclosing04",
enclosingMethod = "<clinit>",
enclosingClazz = notEnclosing04.class
)
class EnclosingLambda {

View File

@ -0,0 +1,47 @@
/*
* 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.
*
* 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.
*/
/*
* @test
* @bug 8336492
* @summary Regression in lambda serialization
*/
public class CaptureVarOrder {
static Object m(String s, int i, Object o) {
return new Object() {
final byte B = 0;
void g() { System.out.println(s + i + B + o); }
};
}
static Runnable r(String s, int i, Object o) {
final byte B = 0;
return () -> System.out.println(s + i + B + o);
}
public static void main(String[] args) throws ReflectiveOperationException {
CaptureVarOrder.class.getDeclaredMethod("lambda$r$0", String.class, int.class, Object.class);
m("", 1, null).getClass().getDeclaredConstructor(String.class, int.class, Object.class);
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.
*
* 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.
*/
/*
* @test
* @bug 8336492
* @summary Regression in lambda serialization
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.*;
public class SerializedLambdaInLocalClass {
public static void main(String[] args) {
SerializedLambdaInLocalClass s = new SerializedLambdaInLocalClass();
s.test(s::f_lambda_in_anon);
s.test(s::f_lambda_in_local);
s.test(s::f_lambda_in_lambda);
}
void test(IntFunction<Supplier<F>> fSupplier) {
try {
F f = fSupplier.apply(42).get();
var baos = new ByteArrayOutputStream();
// write
try (var oos = new ObjectOutputStream(baos)) {
oos.writeObject(f);
}
byte[] bytes = baos.toByteArray();
var bais = new ByteArrayInputStream(bytes);
// read
try (var ois = new ObjectInputStream(bais)) {
F f2 = (F)ois.readObject();
if (f2.getValue() != f.getValue()) {
throw new AssertionError(String.format("Found: %d, expected %d", f2.getValue(), f.getValue()));
}
}
} catch (IOException | ClassNotFoundException ex) {
throw new AssertionError(ex);
}
}
interface F extends Serializable {
int getValue();
}
Supplier<F> f_lambda_in_anon(int x) {
return new Supplier<F>() {
@Override
public F get() {
return () -> x;
}
};
}
Supplier<F> f_lambda_in_local(int x) {
class FSupplier implements Supplier<F> {
@Override
public F get() {
return () -> x;
}
}
return new FSupplier();
}
Supplier<F> f_lambda_in_lambda(int x) {
return () -> () -> x;
}
}