8342967: Lambda deduplication fails with non-metafactory BSMs and mismatched local variables names
Reviewed-by: mcimadamore
This commit is contained in:
parent
b41d713ff4
commit
895a7b64f0
@ -217,7 +217,7 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int hashCode = this.hashCode;
|
int hashCode = this.hashCode;
|
||||||
if (hashCode == 0) {
|
if (hashCode == 0) {
|
||||||
this.hashCode = hashCode = TreeHasher.hash(tree, symbol.params());
|
this.hashCode = hashCode = TreeHasher.hash(types, tree, symbol.params());
|
||||||
}
|
}
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ public class LambdaToMethod extends TreeTranslator {
|
|||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return (o instanceof DedupedLambda dedupedLambda)
|
return (o instanceof DedupedLambda dedupedLambda)
|
||||||
&& types.isSameType(symbol.asType(), dedupedLambda.symbol.asType())
|
&& types.isSameType(symbol.asType(), dedupedLambda.symbol.asType())
|
||||||
&& new TreeDiffer(symbol.params(), dedupedLambda.symbol.params()).scan(tree, dedupedLambda.tree);
|
&& new TreeDiffer(types, symbol.params(), dedupedLambda.symbol.params()).scan(tree, dedupedLambda.tree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -993,7 +993,7 @@ public class TransPatterns extends TreeTranslator {
|
|||||||
!currentNullable &&
|
!currentNullable &&
|
||||||
!previousCompletesNormally &&
|
!previousCompletesNormally &&
|
||||||
!currentCompletesNormally &&
|
!currentCompletesNormally &&
|
||||||
new TreeDiffer(List.of(commonBinding), List.of(currentBinding))
|
new TreeDiffer(types, List.of(commonBinding), List.of(currentBinding))
|
||||||
.scan(commonNestedExpression, currentNestedExpression)) {
|
.scan(commonNestedExpression, currentNestedExpression)) {
|
||||||
accummulator.add(c.head);
|
accummulator.add(c.head);
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,6 +28,9 @@ package com.sun.tools.javac.comp;
|
|||||||
|
|
||||||
import com.sun.tools.javac.code.Flags;
|
import com.sun.tools.javac.code.Flags;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
import com.sun.tools.javac.code.TypeTag;
|
||||||
|
import com.sun.tools.javac.code.Types;
|
||||||
|
import com.sun.tools.javac.jvm.PoolConstant;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
|
import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
|
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
|
||||||
@ -107,10 +110,10 @@ import java.util.Objects;
|
|||||||
|
|
||||||
/** A visitor that compares two lambda bodies for structural equality. */
|
/** A visitor that compares two lambda bodies for structural equality. */
|
||||||
public class TreeDiffer extends TreeScanner {
|
public class TreeDiffer extends TreeScanner {
|
||||||
|
public TreeDiffer(Types types,
|
||||||
public TreeDiffer(
|
Collection<? extends Symbol> symbols, Collection<? extends Symbol> otherSymbols) {
|
||||||
Collection<? extends Symbol> symbols, Collection<? extends Symbol> otherSymbols) {
|
|
||||||
this.equiv = equiv(symbols, otherSymbols);
|
this.equiv = equiv(symbols, otherSymbols);
|
||||||
|
this.types = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<Symbol, Symbol> equiv(
|
private static Map<Symbol, Symbol> equiv(
|
||||||
@ -127,6 +130,7 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
private JCTree parameter;
|
private JCTree parameter;
|
||||||
private boolean result;
|
private boolean result;
|
||||||
private Map<Symbol, Symbol> equiv = new HashMap<>();
|
private Map<Symbol, Symbol> equiv = new HashMap<>();
|
||||||
|
final Types types;
|
||||||
|
|
||||||
public boolean scan(JCTree tree, JCTree parameter) {
|
public boolean scan(JCTree tree, JCTree parameter) {
|
||||||
if (tree == null || parameter == null) {
|
if (tree == null || parameter == null) {
|
||||||
@ -197,13 +201,24 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = tree.sym == that.sym;
|
result = scanSymbol(symbol, otherSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean scanSymbol(Symbol symbol, Symbol otherSymbol) {
|
||||||
|
if (symbol instanceof PoolConstant.Dynamic dms && otherSymbol instanceof PoolConstant.Dynamic other_dms) {
|
||||||
|
return dms.bsmKey(types).equals(other_dms.bsmKey(types));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return symbol == otherSymbol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitSelect(JCFieldAccess tree) {
|
public void visitSelect(JCFieldAccess tree) {
|
||||||
JCFieldAccess that = (JCFieldAccess) parameter;
|
JCFieldAccess that = (JCFieldAccess) parameter;
|
||||||
result = scan(tree.selected, that.selected) && tree.sym == that.sym;
|
|
||||||
|
result = scan(tree.selected, that.selected) &&
|
||||||
|
scanSymbol(tree.sym, that.sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -328,14 +343,7 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitClassDef(JCClassDecl tree) {
|
public void visitClassDef(JCClassDecl tree) {
|
||||||
JCClassDecl that = (JCClassDecl) parameter;
|
result = false;
|
||||||
result =
|
|
||||||
scan(tree.mods, that.mods)
|
|
||||||
&& tree.name == that.name
|
|
||||||
&& scan(tree.typarams, that.typarams)
|
|
||||||
&& scan(tree.extending, that.extending)
|
|
||||||
&& scan(tree.implementing, that.implementing)
|
|
||||||
&& scan(tree.defs, that.defs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -667,14 +675,18 @@ public class TreeDiffer extends TreeScanner {
|
|||||||
JCVariableDecl that = (JCVariableDecl) parameter;
|
JCVariableDecl that = (JCVariableDecl) parameter;
|
||||||
result =
|
result =
|
||||||
scan(tree.mods, that.mods)
|
scan(tree.mods, that.mods)
|
||||||
&& tree.name == that.name
|
|
||||||
&& scan(tree.nameexpr, that.nameexpr)
|
&& scan(tree.nameexpr, that.nameexpr)
|
||||||
&& scan(tree.vartype, that.vartype)
|
&& scan(tree.vartype, that.vartype)
|
||||||
&& scan(tree.init, that.init);
|
&& scan(tree.init, that.init);
|
||||||
if (!result) {
|
|
||||||
return;
|
if (tree.sym.owner.type.hasTag(TypeTag.CLASS)) {
|
||||||
|
// field names are important!
|
||||||
|
result &= tree.name == that.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
equiv.put(tree.sym, that.sym);
|
||||||
}
|
}
|
||||||
equiv.put(tree.sym, that.sym);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,7 +27,10 @@
|
|||||||
package com.sun.tools.javac.comp;
|
package com.sun.tools.javac.comp;
|
||||||
|
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
import com.sun.tools.javac.code.Types;
|
||||||
|
import com.sun.tools.javac.jvm.PoolConstant;
|
||||||
import com.sun.tools.javac.tree.JCTree;
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCIdent;
|
import com.sun.tools.javac.tree.JCTree.JCIdent;
|
||||||
import com.sun.tools.javac.tree.JCTree.JCLiteral;
|
import com.sun.tools.javac.tree.JCTree.JCLiteral;
|
||||||
@ -43,19 +46,21 @@ import java.util.Objects;
|
|||||||
public class TreeHasher extends TreeScanner {
|
public class TreeHasher extends TreeScanner {
|
||||||
|
|
||||||
private final Map<Symbol, Integer> symbolHashes;
|
private final Map<Symbol, Integer> symbolHashes;
|
||||||
|
private final Types types;
|
||||||
private int result = 17;
|
private int result = 17;
|
||||||
|
|
||||||
public TreeHasher(Map<Symbol, Integer> symbolHashes) {
|
public TreeHasher(Types types, Map<Symbol, Integer> symbolHashes) {
|
||||||
this.symbolHashes = Objects.requireNonNull(symbolHashes);
|
this.symbolHashes = Objects.requireNonNull(symbolHashes);
|
||||||
|
this.types = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int hash(JCTree tree, Collection<? extends Symbol> symbols) {
|
public static int hash(Types types, JCTree tree, Collection<? extends Symbol> symbols) {
|
||||||
if (tree == null) {
|
if (tree == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Map<Symbol, Integer> symbolHashes = new HashMap<>();
|
Map<Symbol, Integer> symbolHashes = new HashMap<>();
|
||||||
symbols.forEach(s -> symbolHashes.put(s, symbolHashes.size()));
|
symbols.forEach(s -> symbolHashes.put(s, symbolHashes.size()));
|
||||||
TreeHasher hasher = new TreeHasher(symbolHashes);
|
TreeHasher hasher = new TreeHasher(types, symbolHashes);
|
||||||
tree.accept(hasher);
|
tree.accept(hasher);
|
||||||
return hasher.result;
|
return hasher.result;
|
||||||
}
|
}
|
||||||
@ -87,6 +92,11 @@ public class TreeHasher extends TreeScanner {
|
|||||||
super.visitLiteral(tree);
|
super.visitLiteral(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitClassDef(JCClassDecl tree) {
|
||||||
|
hash(tree.sym);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitIdent(JCIdent tree) {
|
public void visitIdent(JCIdent tree) {
|
||||||
Symbol sym = tree.sym;
|
Symbol sym = tree.sym;
|
||||||
@ -97,15 +107,23 @@ public class TreeHasher extends TreeScanner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash(sym);
|
hashSymbol(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitSelect(JCFieldAccess tree) {
|
public void visitSelect(JCFieldAccess tree) {
|
||||||
hash(tree.sym);
|
hashSymbol(tree.sym);
|
||||||
super.visitSelect(tree);
|
super.visitSelect(tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hashSymbol(Symbol sym) {
|
||||||
|
if (sym instanceof PoolConstant.Dynamic dynamic) {
|
||||||
|
hash(dynamic.bsmKey(types));
|
||||||
|
} else {
|
||||||
|
hash(sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitVarDef(JCVariableDecl tree) {
|
public void visitVarDef(JCVariableDecl tree) {
|
||||||
symbolHashes.computeIfAbsent(tree.sym, k -> symbolHashes.size());
|
symbolHashes.computeIfAbsent(tree.sym, k -> symbolHashes.size());
|
||||||
|
@ -29,52 +29,54 @@ import java.util.function.Function;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class Deduplication {
|
public class Deduplication {
|
||||||
|
void groupEquals(Object... xs) {}
|
||||||
|
void groupNotEquals(Object... xs) {}
|
||||||
void group(Object... xs) {}
|
void group(Object... xs) {}
|
||||||
|
|
||||||
void test() {
|
void test() {
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { ( (Runnable) () -> {} ).run(); },
|
(Runnable) () -> { ( (Runnable) () -> {} ).run(); },
|
||||||
(Runnable) () -> { ( (Runnable) () -> {} ).run(); }
|
(Runnable) () -> { ( (Runnable) () -> {} ).run(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { Deduplication.class.toString(); },
|
(Runnable) () -> { Deduplication.class.toString(); },
|
||||||
(Runnable) () -> { Deduplication.class.toString(); }
|
(Runnable) () -> { Deduplication.class.toString(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { Integer[].class.toString(); },
|
(Runnable) () -> { Integer[].class.toString(); },
|
||||||
(Runnable) () -> { Integer[].class.toString(); }
|
(Runnable) () -> { Integer[].class.toString(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { char.class.toString(); },
|
(Runnable) () -> { char.class.toString(); },
|
||||||
(Runnable) () -> { char.class.toString(); }
|
(Runnable) () -> { char.class.toString(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { Void.class.toString(); },
|
(Runnable) () -> { Void.class.toString(); },
|
||||||
(Runnable) () -> { Void.class.toString(); }
|
(Runnable) () -> { Void.class.toString(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Runnable) () -> { void.class.toString(); },
|
(Runnable) () -> { void.class.toString(); },
|
||||||
(Runnable) () -> { void.class.toString(); }
|
(Runnable) () -> { void.class.toString(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
group((Function<String, Integer>) x -> x.hashCode());
|
groupEquals((Function<String, Integer>) x -> x.hashCode());
|
||||||
group((Function<Object, Integer>) x -> x.hashCode());
|
groupEquals((Function<Object, Integer>) x -> x.hashCode());
|
||||||
|
|
||||||
{
|
{
|
||||||
int x = 1;
|
int x = 1;
|
||||||
group((Supplier<Integer>) () -> x + 1);
|
groupEquals((Supplier<Integer>) () -> x + 1);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int x = 1;
|
int x = 1;
|
||||||
group((Supplier<Integer>) () -> x + 1);
|
groupEquals((Supplier<Integer>) () -> x + 1);
|
||||||
}
|
}
|
||||||
group(
|
groupEquals(
|
||||||
(BiFunction<Integer, Integer, ?>) (x, y) -> x + ((y)),
|
(BiFunction<Integer, Integer, ?>) (x, y) -> x + ((y)),
|
||||||
(BiFunction<Integer, Integer, ?>) (x, y) -> x + (y),
|
(BiFunction<Integer, Integer, ?>) (x, y) -> x + (y),
|
||||||
(BiFunction<Integer, Integer, ?>) (x, y) -> x + y,
|
(BiFunction<Integer, Integer, ?>) (x, y) -> x + y,
|
||||||
@ -85,29 +87,29 @@ public class Deduplication {
|
|||||||
(BiFunction<Integer, Integer, ?>) (x, y) -> ((x)) + (y),
|
(BiFunction<Integer, Integer, ?>) (x, y) -> ((x)) + (y),
|
||||||
(BiFunction<Integer, Integer, ?>) (x, y) -> ((x)) + y);
|
(BiFunction<Integer, Integer, ?>) (x, y) -> ((x)) + y);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Function<Integer, Integer>) x -> x + (1 + 2 + 3),
|
(Function<Integer, Integer>) x -> x + (1 + 2 + 3),
|
||||||
(Function<Integer, Integer>) x -> x + 6);
|
(Function<Integer, Integer>) x -> x + 6);
|
||||||
|
|
||||||
group((Function<Integer, Integer>) x -> x + 1, (Function<Integer, Integer>) y -> y + 1);
|
groupEquals((Function<Integer, Integer>) x -> x + 1, (Function<Integer, Integer>) y -> y + 1);
|
||||||
|
|
||||||
group((Consumer<Integer>) x -> this.f(), (Consumer<Integer>) x -> this.f());
|
groupEquals((Consumer<Integer>) x -> this.f(), (Consumer<Integer>) x -> this.f());
|
||||||
|
|
||||||
group((Consumer<Integer>) y -> this.g());
|
groupEquals((Consumer<Integer>) y -> this.g());
|
||||||
|
|
||||||
group((Consumer<Integer>) x -> f(), (Consumer<Integer>) x -> f());
|
groupEquals((Consumer<Integer>) x -> f(), (Consumer<Integer>) x -> f());
|
||||||
|
|
||||||
group((Consumer<Integer>) y -> g());
|
groupEquals((Consumer<Integer>) y -> g());
|
||||||
|
|
||||||
group((Function<Integer, Integer>) x -> this.i, (Function<Integer, Integer>) x -> this.i);
|
groupEquals((Function<Integer, Integer>) x -> this.i, (Function<Integer, Integer>) x -> this.i);
|
||||||
|
|
||||||
group((Function<Integer, Integer>) y -> this.j);
|
groupEquals((Function<Integer, Integer>) y -> this.j);
|
||||||
|
|
||||||
group((Function<Integer, Integer>) x -> i, (Function<Integer, Integer>) x -> i);
|
groupEquals((Function<Integer, Integer>) x -> i, (Function<Integer, Integer>) x -> i);
|
||||||
|
|
||||||
group((Function<Integer, Integer>) y -> j);
|
groupEquals((Function<Integer, Integer>) y -> j);
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Function<Integer, Integer>)
|
(Function<Integer, Integer>)
|
||||||
y -> {
|
y -> {
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -123,7 +125,7 @@ public class Deduplication {
|
|||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Function<Integer, Integer>)
|
(Function<Integer, Integer>)
|
||||||
x -> {
|
x -> {
|
||||||
int y = x;
|
int y = x;
|
||||||
@ -135,13 +137,13 @@ public class Deduplication {
|
|||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
|
|
||||||
group(
|
groupEquals(
|
||||||
(Function<Integer, Integer>)
|
(Function<Integer, Integer>)
|
||||||
x -> {
|
x -> {
|
||||||
int y = 0, z = x;
|
int y = 0, z = x;
|
||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
group(
|
groupEquals(
|
||||||
(Function<Integer, Integer>)
|
(Function<Integer, Integer>)
|
||||||
x -> {
|
x -> {
|
||||||
int y = 0, z = x;
|
int y = 0, z = x;
|
||||||
@ -154,24 +156,41 @@ public class Deduplication {
|
|||||||
void f() {}
|
void f() {}
|
||||||
|
|
||||||
{
|
{
|
||||||
group((Function<Integer, Integer>) x -> this.i);
|
groupEquals((Function<Integer, Integer>) x -> this.i);
|
||||||
group((Consumer<Integer>) x -> this.f());
|
groupEquals((Consumer<Integer>) x -> this.f());
|
||||||
group((Function<Integer, Integer>) x -> Deduplication.this.i);
|
groupEquals((Function<Integer, Integer>) x -> Deduplication.this.i);
|
||||||
group((Consumer<Integer>) x -> Deduplication.this.f());
|
groupEquals((Consumer<Integer>) x -> Deduplication.this.f());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group((Function<Integer, Integer>) x -> switch (x) { default: yield x; },
|
groupEquals((Function<Integer, Integer>) x -> switch (x) { default: yield x; },
|
||||||
(Function<Integer, Integer>) x -> switch (x) { default: yield x; });
|
(Function<Integer, Integer>) x -> switch (x) { default: yield x; });
|
||||||
|
|
||||||
group((Function<Object, Integer>) x -> x instanceof Integer i ? i : -1,
|
groupEquals((Function<Object, Integer>) x -> x instanceof Integer i ? i : -1,
|
||||||
(Function<Object, Integer>) x -> x instanceof Integer i ? i : -1);
|
(Function<Object, Integer>) x -> x instanceof Integer i ? i : -1);
|
||||||
|
|
||||||
group((Function<Object, Integer>) x -> x instanceof R(var i1, var i2) ? i1 : -1,
|
groupEquals((Function<Object, Integer>) x -> x instanceof R(var i1, var i2) ? i1 : -1,
|
||||||
(Function<Object, Integer>) x -> x instanceof R(var i1, var i2) ? i1 : -1 );
|
(Function<Object, Integer>) x -> x instanceof R(var i1, var i2) ? i1 : -1 );
|
||||||
|
|
||||||
group((Function<Object, Integer>) x -> x instanceof R(Integer i1, int i2) ? i2 : -1,
|
groupEquals((Function<Object, Integer>) x -> x instanceof R(Integer i1, int i2) ? i2 : -1,
|
||||||
(Function<Object, Integer>) x -> x instanceof R(Integer i1, int i2) ? i2 : -1 );
|
(Function<Object, Integer>) x -> x instanceof R(Integer i1, int i2) ? i2 : -1 );
|
||||||
|
|
||||||
|
groupEquals((Function<Object, Integer>) x -> x instanceof int i2 ? i2 : -1,
|
||||||
|
(Function<Object, Integer>) x -> x instanceof int i2 ? i2 : -1);
|
||||||
|
|
||||||
|
groupEquals((Function<Object, Integer>) x -> switch (x) { case String s -> s.length(); default -> -1; },
|
||||||
|
(Function<Object, Integer>) x -> switch (x) { case String s -> s.length(); default -> -1; });
|
||||||
|
|
||||||
|
groupEquals((Function<Object, Integer>) x -> {
|
||||||
|
int y1 = -1;
|
||||||
|
return y1;
|
||||||
|
},
|
||||||
|
(Function<Object, Integer>) x -> {
|
||||||
|
int y2 = -1;
|
||||||
|
return y2;
|
||||||
|
});
|
||||||
|
|
||||||
|
groupNotEquals((Function<Object, Integer>) x -> {class C {} new C(); return 42; }, (Function<Object, Integer>) x -> {class C {} new C(); return 42; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void f() {}
|
void f() {}
|
||||||
|
@ -48,9 +48,11 @@ import java.lang.classfile.*;
|
|||||||
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
|
import java.lang.classfile.attribute.BootstrapMethodsAttribute;
|
||||||
import java.lang.classfile.constantpool.MethodHandleEntry;
|
import java.lang.classfile.constantpool.MethodHandleEntry;
|
||||||
import com.sun.tools.javac.api.ClientCodeWrapper.Trusted;
|
import com.sun.tools.javac.api.ClientCodeWrapper.Trusted;
|
||||||
|
import com.sun.tools.javac.api.JavacTaskImpl;
|
||||||
import com.sun.tools.javac.api.JavacTool;
|
import com.sun.tools.javac.api.JavacTool;
|
||||||
import com.sun.tools.javac.code.Symbol;
|
import com.sun.tools.javac.code.Symbol;
|
||||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||||
|
import com.sun.tools.javac.code.Types;
|
||||||
import com.sun.tools.javac.comp.TreeDiffer;
|
import com.sun.tools.javac.comp.TreeDiffer;
|
||||||
import com.sun.tools.javac.comp.TreeHasher;
|
import com.sun.tools.javac.comp.TreeHasher;
|
||||||
import com.sun.tools.javac.file.JavacFileManager;
|
import com.sun.tools.javac.file.JavacFileManager;
|
||||||
@ -64,6 +66,8 @@ import com.sun.tools.javac.tree.JCTree.Tag;
|
|||||||
import com.sun.tools.javac.tree.TreeScanner;
|
import com.sun.tools.javac.tree.TreeScanner;
|
||||||
import com.sun.tools.javac.util.Context;
|
import com.sun.tools.javac.util.Context;
|
||||||
import com.sun.tools.javac.util.JCDiagnostic;
|
import com.sun.tools.javac.util.JCDiagnostic;
|
||||||
|
import jdk.internal.classfile.impl.BootstrapMethodEntryImpl;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -103,8 +107,11 @@ public class DeduplicationTest {
|
|||||||
"-source", System.getProperty("java.specification.version")),
|
"-source", System.getProperty("java.specification.version")),
|
||||||
null,
|
null,
|
||||||
fileManager.getJavaFileObjects(file));
|
fileManager.getJavaFileObjects(file));
|
||||||
|
|
||||||
|
Context context = ((JavacTaskImpl)task).getContext();
|
||||||
|
Types types = Types.instance(context);
|
||||||
Map<JCLambda, JCLambda> dedupedLambdas = new LinkedHashMap<>();
|
Map<JCLambda, JCLambda> dedupedLambdas = new LinkedHashMap<>();
|
||||||
task.addTaskListener(new TreeDiffHashTaskListener(dedupedLambdas));
|
task.addTaskListener(new TreeDiffHashTaskListener(dedupedLambdas, types));
|
||||||
Iterable<? extends JavaFileObject> generated = task.generate();
|
Iterable<? extends JavaFileObject> generated = task.generate();
|
||||||
if (!diagnosticListener.unexpected.isEmpty()) {
|
if (!diagnosticListener.unexpected.isEmpty()) {
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
@ -142,15 +149,19 @@ public class DeduplicationTest {
|
|||||||
try (InputStream input = output.openInputStream()) {
|
try (InputStream input = output.openInputStream()) {
|
||||||
cm = ClassFile.of().parse(input.readAllBytes());
|
cm = ClassFile.of().parse(input.readAllBytes());
|
||||||
}
|
}
|
||||||
if (cm.thisClass().asInternalName().equals("com/sun/tools/javac/comp/Deduplication$R")) {
|
if (cm.thisClass().asInternalName().equals("com/sun/tools/javac/comp/Deduplication$R") ||
|
||||||
|
cm.thisClass().asInternalName().equals("com/sun/tools/javac/comp/Deduplication$1C") ||
|
||||||
|
cm.thisClass().asInternalName().equals("com/sun/tools/javac/comp/Deduplication$2C")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
BootstrapMethodsAttribute bsm = cm.findAttribute(Attributes.bootstrapMethods()).orElseThrow();
|
BootstrapMethodsAttribute bsm = cm.findAttribute(Attributes.bootstrapMethods()).orElseThrow();
|
||||||
for (BootstrapMethodEntry b : bsm.bootstrapMethods()) {
|
for (BootstrapMethodEntry b : bsm.bootstrapMethods()) {
|
||||||
bootstrapMethodNames.add(
|
if (((BootstrapMethodEntryImpl) b).bootstrapMethod().asSymbol().methodName().equals("metafactory")) {
|
||||||
((MethodHandleEntry)b.arguments().get(1))
|
bootstrapMethodNames.add(
|
||||||
.reference()
|
((MethodHandleEntry) b.arguments().get(1))
|
||||||
.name().stringValue());
|
.reference()
|
||||||
|
.name().stringValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<String> deduplicatedNames =
|
Set<String> deduplicatedNames =
|
||||||
@ -249,9 +260,11 @@ public class DeduplicationTest {
|
|||||||
* deduplicated to.
|
* deduplicated to.
|
||||||
*/
|
*/
|
||||||
private final Map<JCLambda, JCLambda> dedupedLambdas;
|
private final Map<JCLambda, JCLambda> dedupedLambdas;
|
||||||
|
private final Types types;
|
||||||
|
|
||||||
public TreeDiffHashTaskListener(Map<JCLambda, JCLambda> dedupedLambdas) {
|
public TreeDiffHashTaskListener(Map<JCLambda, JCLambda> dedupedLambdas, Types types) {
|
||||||
this.dedupedLambdas = dedupedLambdas;
|
this.dedupedLambdas = dedupedLambdas;
|
||||||
|
this.types = types;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -262,31 +275,26 @@ public class DeduplicationTest {
|
|||||||
// Scan the compilation for calls to a varargs method named 'group', whose arguments
|
// Scan the compilation for calls to a varargs method named 'group', whose arguments
|
||||||
// are a group of lambdas that are equivalent to each other, but distinct from all
|
// are a group of lambdas that are equivalent to each other, but distinct from all
|
||||||
// lambdas in the compilation unit outside of that group.
|
// lambdas in the compilation unit outside of that group.
|
||||||
List<List<JCLambda>> lambdaGroups = new ArrayList<>();
|
List<List<JCLambda>> lambdaEqualsGroups = new ArrayList<>();
|
||||||
|
List<List<JCLambda>> lambdaNotEqualsGroups = new ArrayList<>();
|
||||||
|
|
||||||
new TreeScanner() {
|
new TreeScanner() {
|
||||||
@Override
|
@Override
|
||||||
public void visitApply(JCMethodInvocation tree) {
|
public void visitApply(JCMethodInvocation tree) {
|
||||||
if (tree.getMethodSelect().getTag() == Tag.IDENT
|
if (isMethodWithName(tree, "groupEquals")) {
|
||||||
&& ((JCIdent) tree.getMethodSelect())
|
addToGroup(tree, lambdaEqualsGroups);
|
||||||
.getName()
|
} else if (isMethodWithName(tree, "groupNotEquals")) {
|
||||||
.contentEquals("group")) {
|
addToGroup(tree, lambdaNotEqualsGroups);
|
||||||
List<JCLambda> xs = new ArrayList<>();
|
|
||||||
for (JCExpression arg : tree.getArguments()) {
|
|
||||||
if (arg instanceof JCTypeCast) {
|
|
||||||
arg = ((JCTypeCast) arg).getExpression();
|
|
||||||
}
|
|
||||||
xs.add((JCLambda) arg);
|
|
||||||
}
|
|
||||||
lambdaGroups.add(xs);
|
|
||||||
}
|
}
|
||||||
super.visitApply(tree);
|
super.visitApply(tree);
|
||||||
}
|
}
|
||||||
}.scan((JCCompilationUnit) e.getCompilationUnit());
|
}.scan((JCCompilationUnit) e.getCompilationUnit());
|
||||||
for (int i = 0; i < lambdaGroups.size(); i++) {
|
|
||||||
List<JCLambda> curr = lambdaGroups.get(i);
|
for (int i = 0; i < lambdaEqualsGroups.size(); i++) {
|
||||||
JCLambda first = null;
|
List<JCLambda> curr = lambdaEqualsGroups.get(i);
|
||||||
// Assert that all pairwise combinations of lambdas in the group are equal, and
|
// Assert that all pairwise combinations of lambdas in the group are equal, and
|
||||||
// hash to the same value.
|
// hash to the same value.
|
||||||
|
JCLambda first = null;
|
||||||
for (JCLambda lhs : curr) {
|
for (JCLambda lhs : curr) {
|
||||||
if (first == null) {
|
if (first == null) {
|
||||||
first = lhs;
|
first = lhs;
|
||||||
@ -294,18 +302,20 @@ public class DeduplicationTest {
|
|||||||
dedupedLambdas.put(lhs, first);
|
dedupedLambdas.put(lhs, first);
|
||||||
}
|
}
|
||||||
for (JCLambda rhs : curr) {
|
for (JCLambda rhs : curr) {
|
||||||
if (!new TreeDiffer(paramSymbols(lhs), paramSymbols(rhs))
|
if (rhs != lhs) {
|
||||||
.scan(lhs.body, rhs.body)) {
|
if (!new TreeDiffer(types, paramSymbols(lhs), paramSymbols(rhs))
|
||||||
throw new AssertionError(
|
.scan(lhs.body, rhs.body)) {
|
||||||
String.format(
|
throw new AssertionError(
|
||||||
"expected lambdas to be equal\n%s\n%s", lhs, rhs));
|
String.format(
|
||||||
}
|
"expected lambdas to be equal\n%s\n%s", lhs, rhs));
|
||||||
if (TreeHasher.hash(lhs, paramSymbols(lhs))
|
}
|
||||||
!= TreeHasher.hash(rhs, paramSymbols(rhs))) {
|
if (TreeHasher.hash(types, lhs, paramSymbols(lhs))
|
||||||
throw new AssertionError(
|
!= TreeHasher.hash(types, rhs, paramSymbols(rhs))) {
|
||||||
String.format(
|
throw new AssertionError(
|
||||||
"expected lambdas to hash to the same value\n%s\n%s",
|
String.format(
|
||||||
lhs, rhs));
|
"expected lambdas to hash to the same value\n%s\n%s",
|
||||||
|
lhs, rhs));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,31 +323,61 @@ public class DeduplicationTest {
|
|||||||
// or hash to the same value as lambda outside the group.
|
// or hash to the same value as lambda outside the group.
|
||||||
// (Note that the hash collisions won't result in correctness problems but could
|
// (Note that the hash collisions won't result in correctness problems but could
|
||||||
// regress performs, and do not currently occurr for any of the test inputs.)
|
// regress performs, and do not currently occurr for any of the test inputs.)
|
||||||
for (int j = 0; j < lambdaGroups.size(); j++) {
|
assertNotEqualsWithinGroup(lambdaEqualsGroups, i, curr, types);
|
||||||
if (i == j) {
|
}
|
||||||
continue;
|
lambdaEqualsGroups.clear();
|
||||||
}
|
|
||||||
for (JCLambda lhs : curr) {
|
// Assert that no lambdas in a not-equals group are equal to any lambdas inside that group,
|
||||||
for (JCLambda rhs : lambdaGroups.get(j)) {
|
// or hash to the same value as lambda inside the group.
|
||||||
if (new TreeDiffer(paramSymbols(lhs), paramSymbols(rhs))
|
for (int i = 0; i < lambdaNotEqualsGroups.size(); i++) {
|
||||||
.scan(lhs.body, rhs.body)) {
|
List<JCLambda> curr = lambdaNotEqualsGroups.get(i);
|
||||||
throw new AssertionError(
|
|
||||||
String.format(
|
assertNotEqualsWithinGroup(lambdaNotEqualsGroups, i, curr, types);
|
||||||
"expected lambdas to not be equal\n%s\n%s",
|
}
|
||||||
lhs, rhs));
|
lambdaNotEqualsGroups.clear();
|
||||||
}
|
}
|
||||||
if (TreeHasher.hash(lhs, paramSymbols(lhs))
|
|
||||||
== TreeHasher.hash(rhs, paramSymbols(rhs))) {
|
private void assertNotEqualsWithinGroup(List<List<JCLambda>> lambdaNotEqualsGroups, int i, List<JCLambda> curr, Types types) {
|
||||||
throw new AssertionError(
|
for (int j = 0; j < lambdaNotEqualsGroups.size(); j++) {
|
||||||
String.format(
|
if (i == j) {
|
||||||
"expected lambdas to hash to different values\n%s\n%s",
|
continue;
|
||||||
lhs, rhs));
|
}
|
||||||
}
|
for (JCLambda lhs : curr) {
|
||||||
|
for (JCLambda rhs : lambdaNotEqualsGroups.get(j)) {
|
||||||
|
if (new TreeDiffer(types, paramSymbols(lhs), paramSymbols(rhs))
|
||||||
|
.scan(lhs.body, rhs.body)) {
|
||||||
|
throw new AssertionError(
|
||||||
|
String.format(
|
||||||
|
"expected lambdas to not be equal\n%s\n%s",
|
||||||
|
lhs, rhs));
|
||||||
|
}
|
||||||
|
if (TreeHasher.hash(types, lhs, paramSymbols(lhs))
|
||||||
|
== TreeHasher.hash(types, rhs, paramSymbols(rhs))) {
|
||||||
|
throw new AssertionError(
|
||||||
|
String.format(
|
||||||
|
"expected lambdas to hash to different values\n%s\n%s",
|
||||||
|
lhs, rhs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lambdaGroups.clear();
|
}
|
||||||
|
|
||||||
|
private boolean isMethodWithName(JCMethodInvocation tree, String markerMethodName) {
|
||||||
|
return tree.getMethodSelect().getTag() == Tag.IDENT && ((JCIdent) tree.getMethodSelect())
|
||||||
|
.getName()
|
||||||
|
.contentEquals(markerMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToGroup(JCMethodInvocation tree, List<List<JCLambda>> groupToAdd) {
|
||||||
|
List<JCLambda> xs = new ArrayList<>();
|
||||||
|
for (JCExpression arg : tree.getArguments()) {
|
||||||
|
if (arg instanceof JCTypeCast) {
|
||||||
|
arg = ((JCTypeCast) arg).getExpression();
|
||||||
|
}
|
||||||
|
xs.add((JCLambda) arg);
|
||||||
|
}
|
||||||
|
groupToAdd.add(xs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user