--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Mon Mar 26 21:30:33 2018 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Mar 27 13:48:16 2018 -0400
@@ -56,6 +56,7 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -113,6 +114,15 @@
/** force serializable representation, for stress testing **/
private final boolean forceSerializable;
+ /** true if line or local variable debug info has been requested */
+ private final boolean debugLinesOrVars;
+
+ /** dump statistics about lambda method deduplication */
+ private final boolean verboseDeduplication;
+
+ /** deduplicate lambda implementation methods */
+ private final boolean deduplicateLambdas;
+
/** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
public static final int FLAG_SERIALIZABLE = 1 << 0;
@@ -149,9 +159,64 @@
dumpLambdaToMethodStats = options.isSet("debug.dumpLambdaToMethodStats");
attr = Attr.instance(context);
forceSerializable = options.isSet("forceSerializable");
+ debugLinesOrVars = options.isSet(Option.G)
+ || options.isSet(Option.G_CUSTOM, "lines")
+ || options.isSet(Option.G_CUSTOM, "vars");
+ verboseDeduplication = options.isSet("debug.dumpLambdaToMethodDeduplication");
+ deduplicateLambdas = options.getBoolean("deduplicateLambdas", true);
}
// </editor-fold>
+ class DedupedLambda {
+ private final MethodSymbol symbol;
+ private final JCTree tree;
+
+ private int hashCode;
+
+ DedupedLambda(MethodSymbol symbol, JCTree tree) {
+ this.symbol = symbol;
+ this.tree = tree;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int hashCode = this.hashCode;
+ if (hashCode == 0) {
+ this.hashCode = hashCode = TreeHasher.hash(tree, sym -> {
+ if (sym.owner == symbol) {
+ int idx = symbol.params().indexOf(sym);
+ if (idx != -1) {
+ return idx;
+ }
+ }
+ return null;
+ });
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DedupedLambda)) {
+ return false;
+ }
+ DedupedLambda that = (DedupedLambda) o;
+ return types.isSameType(symbol.asType(), that.symbol.asType())
+ && new TreeDiffer((lhs, rhs) -> {
+ if (lhs.owner == symbol) {
+ int idx = symbol.params().indexOf(lhs);
+ if (idx != -1) {
+ if (Objects.equals(idx, that.symbol.params().indexOf(rhs))) {
+ return true;
+ }
+ }
+ }
+ return null;
+ }).scan(tree, that.tree);
+ }
+ }
+
private class KlassInfo {
/**
@@ -159,6 +224,8 @@
*/
private ListBuffer<JCTree> appendedMethodList;
+ private Map<DedupedLambda, DedupedLambda> dedupedLambdas;
+
/**
* list of deserialization cases
*/
@@ -179,6 +246,7 @@
private KlassInfo(JCClassDecl clazz) {
this.clazz = clazz;
appendedMethodList = new ListBuffer<>();
+ dedupedLambdas = new HashMap<>();
deserializeCases = new HashMap<>();
MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
List.nil(), syms.methodClass);
@@ -329,8 +397,20 @@
//captured members directly).
lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
- //Add the method to the list of methods to be added to this class.
- kInfo.addMethod(lambdaDecl);
+ boolean dedupe = false;
+ if (deduplicateLambdas && !debugLinesOrVars && !localContext.isSerializable()) {
+ DedupedLambda dedupedLambda = new DedupedLambda(lambdaDecl.sym, lambdaDecl.body);
+ DedupedLambda existing = kInfo.dedupedLambdas.putIfAbsent(dedupedLambda, dedupedLambda);
+ if (existing != null) {
+ sym = existing.symbol;
+ dedupe = true;
+ if (verboseDeduplication) log.note(tree, Notes.VerboseL2mDeduplicate(sym));
+ }
+ }
+ if (!dedupe) {
+ //Add the method to the list of methods to be added to this class.
+ kInfo.addMethod(lambdaDecl);
+ }
//now that we have generated a method for the lambda expression,
//we can translate the lambda into a method reference pointing to the newly