From b8fdfa467de2b61b8e6fa0bdc2b4c79e8bb5576e Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Sat, 9 Jan 2016 01:27:58 -0500 Subject: [PATCH] Detect and omit the JVM's lambda factory method. --- .../retrolambda/test/LambdaClassesTest.java | 32 ++++++++++++++++--- .../lambdas/BackportLambdaClass.java | 3 ++ .../retrolambda/lambdas/LambdaNaming.java | 6 ++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/end-to-end-tests/src/test/java/net/orfjackal/retrolambda/test/LambdaClassesTest.java b/end-to-end-tests/src/test/java/net/orfjackal/retrolambda/test/LambdaClassesTest.java index 173e1671..12856471 100644 --- a/end-to-end-tests/src/test/java/net/orfjackal/retrolambda/test/LambdaClassesTest.java +++ b/end-to-end-tests/src/test/java/net/orfjackal/retrolambda/test/LambdaClassesTest.java @@ -6,7 +6,11 @@ import org.junit.Test; +import java.lang.reflect.Method; +import java.util.*; + import static net.orfjackal.retrolambda.test.TestUtil.assertClassExists; +import static org.junit.Assert.assertTrue; public class LambdaClassesTest { @@ -18,10 +22,31 @@ public void the_sequence_number_starts_from_1_for_each_enclosing_class() { assertClassExists(Dummy2.class.getName() + "$$Lambda$2"); } + @Test + public void capturing_lambda_contain_no_unexpected_methods() throws ClassNotFoundException { + List expected = new ArrayList<>(Arrays.asList("lambdaFactory$", "run")); + Class cls = Class.forName(Dummy1.class.getName() + "$$Lambda$1"); + for (Method method : cls.getDeclaredMethods()) { + assertTrue(method.getName() + " not expected", expected.remove(method.getName())); + } + assertTrue("Missing methods: " + expected, expected.isEmpty()); + } + + @Test + public void non_capturing_lambda_contain_no_unexpected_methods() throws ClassNotFoundException { + List expected = new ArrayList<>(Arrays.asList("lambdaFactory$", "run")); + Class cls = Class.forName(Dummy2.class.getName() + "$$Lambda$1"); + for (Method method : cls.getDeclaredMethods()) { + assertTrue(method.getName() + " not expected", expected.remove(method.getName())); + } + assertTrue("Missing methods: " + expected, expected.isEmpty()); + } + @SuppressWarnings("UnusedDeclaration") private class Dummy1 { private Dummy1() { + // Non-capturing lambdas Runnable lambda1 = () -> { }; Runnable lambda2 = () -> { @@ -32,10 +57,9 @@ private Dummy1() { @SuppressWarnings("UnusedDeclaration") private class Dummy2 { private Dummy2() { - Runnable lambda1 = () -> { - }; - Runnable lambda2 = () -> { - }; + // Capturing lambdas + Runnable lambda1 = () -> System.out.println(hashCode()); + Runnable lambda2 = () -> System.out.println(hashCode()); } } } diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaClass.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaClass.java index b58aa0d4..4522c13e 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaClass.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaClass.java @@ -45,6 +45,9 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si if (LambdaNaming.isSerializationHook(access, name, desc)) { return null; // remove serialization hooks; we serialize lambda instances as-is } + if (LambdaNaming.isPlatformFactoryMethod(access, name, desc, factoryMethod.getDesc())) { + return null; // remove the JVM's factory method which will not be unused + } MethodVisitor next = super.visitMethod(access, name, desc, signature, exceptions); next = new RemoveMagicLambdaConstructorCall(next); next = new CallPrivateImplMethodsViaAccessMethods(next); diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java index 9e824045..13d15dc3 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java @@ -31,4 +31,10 @@ public static boolean isDeserializationHook(int access, String name, String desc && desc.equals("(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;") && Flags.hasFlag(access, ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC); } + + public static boolean isPlatformFactoryMethod(int access, String name, String desc, String targetDesc) { + return name.equals("get$Lambda") + && desc.equals(targetDesc) + && Flags.hasFlag(access, ACC_PRIVATE | ACC_STATIC); + } }