src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
changeset 49429 752ecccb0b7f
parent 48721 ef3557eb4306
child 49541 4f6887eade94
--- 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