20 * or visit www.oracle.com if you need additional information or have any |
20 * or visit www.oracle.com if you need additional information or have any |
21 * questions. |
21 * questions. |
22 */ |
22 */ |
23 |
23 |
24 /** |
24 /** |
25 * @test 8200301 |
25 * @test 8200301 8201194 |
26 * @summary deduplicate lambda methods with the same body, target type, and captured state |
26 * @summary deduplicate lambda methods with the same body, target type, and captured state |
27 * @modules jdk.jdeps/com.sun.tools.classfile jdk.compiler/com.sun.tools.javac.api |
27 * @modules jdk.jdeps/com.sun.tools.classfile jdk.compiler/com.sun.tools.javac.api |
28 * jdk.compiler/com.sun.tools.javac.code jdk.compiler/com.sun.tools.javac.comp |
28 * jdk.compiler/com.sun.tools.javac.code jdk.compiler/com.sun.tools.javac.comp |
29 * jdk.compiler/com.sun.tools.javac.file jdk.compiler/com.sun.tools.javac.main |
29 * jdk.compiler/com.sun.tools.javac.file jdk.compiler/com.sun.tools.javac.main |
30 * jdk.compiler/com.sun.tools.javac.tree jdk.compiler/com.sun.tools.javac.util |
30 * jdk.compiler/com.sun.tools.javac.tree jdk.compiler/com.sun.tools.javac.util |
31 * @run main DeduplicationTest |
31 * @run main DeduplicationTest |
32 */ |
32 */ |
33 import static java.nio.charset.StandardCharsets.UTF_8; |
33 import static java.nio.charset.StandardCharsets.UTF_8; |
34 import static java.util.stream.Collectors.joining; |
34 import static java.util.stream.Collectors.joining; |
|
35 import static java.util.stream.Collectors.toList; |
35 import static java.util.stream.Collectors.toMap; |
36 import static java.util.stream.Collectors.toMap; |
36 import static java.util.stream.Collectors.toSet; |
37 import static java.util.stream.Collectors.toSet; |
37 |
38 |
38 import com.sun.source.util.JavacTask; |
39 import com.sun.source.util.JavacTask; |
39 import com.sun.source.util.TaskEvent; |
40 import com.sun.source.util.TaskEvent; |
55 import com.sun.tools.javac.tree.JCTree.JCExpression; |
56 import com.sun.tools.javac.tree.JCTree.JCExpression; |
56 import com.sun.tools.javac.tree.JCTree.JCIdent; |
57 import com.sun.tools.javac.tree.JCTree.JCIdent; |
57 import com.sun.tools.javac.tree.JCTree.JCLambda; |
58 import com.sun.tools.javac.tree.JCTree.JCLambda; |
58 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; |
59 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; |
59 import com.sun.tools.javac.tree.JCTree.JCTypeCast; |
60 import com.sun.tools.javac.tree.JCTree.JCTypeCast; |
60 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; |
|
61 import com.sun.tools.javac.tree.JCTree.Tag; |
61 import com.sun.tools.javac.tree.JCTree.Tag; |
62 import com.sun.tools.javac.tree.TreeScanner; |
62 import com.sun.tools.javac.tree.TreeScanner; |
63 import com.sun.tools.javac.util.Context; |
63 import com.sun.tools.javac.util.Context; |
64 import com.sun.tools.javac.util.JCDiagnostic; |
64 import com.sun.tools.javac.util.JCDiagnostic; |
65 import java.nio.file.Path; |
65 import java.nio.file.Path; |
68 import java.util.Arrays; |
68 import java.util.Arrays; |
69 import java.util.LinkedHashMap; |
69 import java.util.LinkedHashMap; |
70 import java.util.List; |
70 import java.util.List; |
71 import java.util.Locale; |
71 import java.util.Locale; |
72 import java.util.Map; |
72 import java.util.Map; |
73 import java.util.Objects; |
|
74 import java.util.Set; |
73 import java.util.Set; |
75 import java.util.TreeSet; |
74 import java.util.TreeSet; |
76 import java.util.function.BiFunction; |
|
77 import javax.tools.Diagnostic; |
75 import javax.tools.Diagnostic; |
78 import javax.tools.DiagnosticListener; |
76 import javax.tools.DiagnosticListener; |
79 import javax.tools.JavaFileObject; |
77 import javax.tools.JavaFileObject; |
80 |
78 |
81 public class DeduplicationTest { |
79 public class DeduplicationTest { |
158 "expected deduplicated methods: %s, but saw: %s", |
156 "expected deduplicated methods: %s, but saw: %s", |
159 deduplicatedNames, bootstrapMethodNames)); |
157 deduplicatedNames, bootstrapMethodNames)); |
160 } |
158 } |
161 } |
159 } |
162 |
160 |
163 /** |
161 /** Returns the parameter symbols of the given lambda. */ |
164 * Returns a symbol comparator that treats symbols that correspond to the same parameter of each |
162 private static List<Symbol> paramSymbols(JCLambda lambda) { |
165 * of the given lambdas as equal. |
163 return lambda.params.stream().map(x -> x.sym).collect(toList()); |
166 */ |
|
167 private static BiFunction<Symbol, Symbol, Boolean> paramsEqual(JCLambda lhs, JCLambda rhs) { |
|
168 return (x, y) -> { |
|
169 Integer idx = paramIndex(lhs, x); |
|
170 if (idx != null && idx != -1) { |
|
171 if (Objects.equals(idx, paramIndex(rhs, y))) { |
|
172 return true; |
|
173 } |
|
174 } |
|
175 return null; |
|
176 }; |
|
177 } |
|
178 |
|
179 /** |
|
180 * Returns the index of the given symbol as a parameter of the given lambda, or else {@code -1} |
|
181 * if is not a parameter. |
|
182 */ |
|
183 private static Integer paramIndex(JCLambda lambda, Symbol sym) { |
|
184 if (sym != null) { |
|
185 int idx = 0; |
|
186 for (JCVariableDecl param : lambda.params) { |
|
187 if (sym == param.sym) { |
|
188 return idx; |
|
189 } |
|
190 } |
|
191 } |
|
192 return null; |
|
193 } |
164 } |
194 |
165 |
195 /** A diagnostic listener that records debug messages related to lambda desugaring. */ |
166 /** A diagnostic listener that records debug messages related to lambda desugaring. */ |
196 @Trusted |
167 @Trusted |
197 static class Listener implements DiagnosticListener<JavaFileObject> { |
168 static class Listener implements DiagnosticListener<JavaFileObject> { |
308 first = lhs; |
279 first = lhs; |
309 } else { |
280 } else { |
310 dedupedLambdas.put(lhs, first); |
281 dedupedLambdas.put(lhs, first); |
311 } |
282 } |
312 for (JCLambda rhs : curr) { |
283 for (JCLambda rhs : curr) { |
313 if (!new TreeDiffer(paramsEqual(lhs, rhs)).scan(lhs.body, rhs.body)) { |
284 if (!new TreeDiffer(paramSymbols(lhs), paramSymbols(rhs)) |
|
285 .scan(lhs.body, rhs.body)) { |
314 throw new AssertionError( |
286 throw new AssertionError( |
315 String.format( |
287 String.format( |
316 "expected lambdas to be equal\n%s\n%s", lhs, rhs)); |
288 "expected lambdas to be equal\n%s\n%s", lhs, rhs)); |
317 } |
289 } |
318 if (TreeHasher.hash(lhs, sym -> paramIndex(lhs, sym)) |
290 if (TreeHasher.hash(lhs, paramSymbols(lhs)) |
319 != TreeHasher.hash(rhs, sym -> paramIndex(rhs, sym))) { |
291 != TreeHasher.hash(rhs, paramSymbols(rhs))) { |
320 throw new AssertionError( |
292 throw new AssertionError( |
321 String.format( |
293 String.format( |
322 "expected lambdas to hash to the same value\n%s\n%s", |
294 "expected lambdas to hash to the same value\n%s\n%s", |
323 lhs, rhs)); |
295 lhs, rhs)); |
324 } |
296 } |
332 if (i == j) { |
304 if (i == j) { |
333 continue; |
305 continue; |
334 } |
306 } |
335 for (JCLambda lhs : curr) { |
307 for (JCLambda lhs : curr) { |
336 for (JCLambda rhs : lambdaGroups.get(j)) { |
308 for (JCLambda rhs : lambdaGroups.get(j)) { |
337 if (new TreeDiffer(paramsEqual(lhs, rhs)).scan(lhs.body, rhs.body)) { |
309 if (new TreeDiffer(paramSymbols(lhs), paramSymbols(rhs)) |
|
310 .scan(lhs.body, rhs.body)) { |
338 throw new AssertionError( |
311 throw new AssertionError( |
339 String.format( |
312 String.format( |
340 "expected lambdas to not be equal\n%s\n%s", |
313 "expected lambdas to not be equal\n%s\n%s", |
341 lhs, rhs)); |
314 lhs, rhs)); |
342 } |
315 } |
343 if (TreeHasher.hash(lhs, sym -> paramIndex(lhs, sym)) |
316 if (TreeHasher.hash(lhs, paramSymbols(lhs)) |
344 == TreeHasher.hash(rhs, sym -> paramIndex(rhs, sym))) { |
317 == TreeHasher.hash(rhs, paramSymbols(rhs))) { |
345 throw new AssertionError( |
318 throw new AssertionError( |
346 String.format( |
319 String.format( |
347 "expected lambdas to hash to different values\n%s\n%s", |
320 "expected lambdas to hash to different values\n%s\n%s", |
348 lhs, rhs)); |
321 lhs, rhs)); |
349 } |
322 } |