8336492: Regression in lambda serialization
Reviewed-by: vromero
This commit is contained in:
parent
358ff19633
commit
8a4ea09fa2
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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)
|
||||
|
@ -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*/)
|
||||
|
@ -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"),
|
||||
|
@ -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 {
|
||||
|
47
test/langtools/tools/javac/lambda/CaptureVarOrder.java
Normal file
47
test/langtools/tools/javac/lambda/CaptureVarOrder.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user