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 7fdfd93f..2341db55 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 @@ -4,6 +4,7 @@ package net.orfjackal.retrolambda.test; +import com.google.common.collect.ImmutableSet; import org.junit.Test; import java.lang.reflect.Method; @@ -85,4 +86,24 @@ private NonCapturing() { }; } } + + + @Test + public void lambda_bodies_contain_no_unnecessary_methods() throws ClassNotFoundException { + Set expected = ImmutableSet.of("lambda$main$0", "main"); + + Set actual = new HashSet<>(); + for (Method method : HasLambdaBody.class.getDeclaredMethods()) { + actual.add(method.getName()); + } + assertThat(actual, is(expected)); + } + + @SuppressWarnings("UnusedDeclaration") + private class HasLambdaBody { + private void main() { + Runnable lambda = () -> { + }; + } + } } diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/RemoveDefaultMethodBodies.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/RemoveDefaultMethodBodies.java index 9343f4c4..c5041448 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/RemoveDefaultMethodBodies.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/RemoveDefaultMethodBodies.java @@ -4,6 +4,7 @@ package net.orfjackal.retrolambda.interfaces; +import net.orfjackal.retrolambda.lambdas.LambdaNaming; import org.objectweb.asm.*; import org.objectweb.asm.tree.MethodNode; @@ -18,7 +19,8 @@ public RemoveDefaultMethodBodies(ClassVisitor next) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if (isPrivateInstanceMethod(access)) { // lambda impl methods which capture `this` are private instance methods + if (LambdaNaming.isBodyMethod(access, name)) { + // lambda impl methods which capture `this` are synthetic instance methods return null; } if (isDefaultMethod(access)) { @@ -29,10 +31,6 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si } } - private static boolean isPrivateInstanceMethod(int access) { - return isPrivateMethod(access) && isInstanceMethod(access); - } - private static boolean isDefaultMethod(int access) { return isConcreteMethod(access) && isInstanceMethod(access); } diff --git a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaInvocations.java b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaInvocations.java index 17ee2cd6..cf43363c 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaInvocations.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaInvocations.java @@ -48,6 +48,10 @@ private static void resetLambdaClassSequenceNumber() { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (LambdaNaming.isBodyMethod(access, name)) { + // Ensure our generated lambda class is able to call this method. + access &= ~ACC_PRIVATE; + } if (LambdaNaming.isDeserializationHook(access, name, desc)) { return null; // remove serialization hooks; we serialize lambda instances as-is } @@ -62,6 +66,13 @@ Handle getLambdaAccessMethod(Handle implMethod) { // the method will be relocated to a companion class return implMethod; } + if (LambdaNaming.isBodyMethodName(implMethod.getName())) { + if (implMethod.getTag() == H_INVOKESPECIAL) { + // The private body method is now package so switch its invocation from special to virtual. + return new Handle(H_INVOKEVIRTUAL, implMethod.getOwner(), implMethod.getName(), implMethod.getDesc()); + } + return implMethod; + } // TODO: do not generate an access method if the impl method is not private (probably not implementable with a single pass) String name = "access$lambda$" + lambdaAccessToImplMethods.size(); String desc = getLambdaAccessMethodDesc(implMethod); 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 13d15dc3..c9fafd68 100644 --- a/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java +++ b/retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java @@ -37,4 +37,12 @@ public static boolean isPlatformFactoryMethod(int access, String name, String de && desc.equals(targetDesc) && Flags.hasFlag(access, ACC_PRIVATE | ACC_STATIC); } + + public static boolean isBodyMethodName(String name) { + return name.startsWith("lambda$"); + } + + public static boolean isBodyMethod(int access, String name) { + return isBodyMethodName(name) && Flags.hasFlag(access, ACC_SYNTHETIC); + } }