Skip to content

Commit c0d2e45

Browse files
committed
Skip access method when lambda body method can be promoted.
1 parent 365b819 commit c0d2e45

File tree

4 files changed

+43
-5
lines changed

4 files changed

+43
-5
lines changed

end-to-end-tests/src/test/java/net/orfjackal/retrolambda/test/LambdaClassesTest.java

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package net.orfjackal.retrolambda.test;
66

7+
import com.google.common.collect.ImmutableSet;
78
import org.junit.Test;
89

910
import java.lang.reflect.Method;
@@ -85,4 +86,24 @@ private NonCapturing() {
8586
};
8687
}
8788
}
89+
90+
91+
@Test
92+
public void lambda_bodies_contain_no_unnecessary_methods() throws ClassNotFoundException {
93+
Set<String> expected = ImmutableSet.of("lambda$main$18", "main");
94+
95+
Set<String> actual = new HashSet<>();
96+
for (Method method : HasLambdaBody.class.getDeclaredMethods()) {
97+
actual.add(method.getName());
98+
}
99+
assertThat(actual, is(expected));
100+
}
101+
102+
@SuppressWarnings("UnusedDeclaration")
103+
private class HasLambdaBody {
104+
private void main() {
105+
Runnable lambda = () -> {
106+
};
107+
}
108+
}
88109
}

retrolambda/src/main/java/net/orfjackal/retrolambda/interfaces/RemoveDefaultMethodBodies.java

+3-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package net.orfjackal.retrolambda.interfaces;
66

7+
import net.orfjackal.retrolambda.lambdas.LambdaNaming;
78
import org.objectweb.asm.*;
89
import org.objectweb.asm.tree.MethodNode;
910

@@ -18,7 +19,8 @@ public RemoveDefaultMethodBodies(ClassVisitor next) {
1819

1920
@Override
2021
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
21-
if (isPrivateInstanceMethod(access)) { // lambda impl methods which capture `this` are private instance methods
22+
if (LambdaNaming.isBodyMethod(access, name)) {
23+
// lambda impl methods which capture `this` are synthetic instance methods
2224
return null;
2325
}
2426
if (isDefaultMethod(access)) {
@@ -29,10 +31,6 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
2931
}
3032
}
3133

32-
private static boolean isPrivateInstanceMethod(int access) {
33-
return isPrivateMethod(access) && isInstanceMethod(access);
34-
}
35-
3634
private static boolean isDefaultMethod(int access) {
3735
return isConcreteMethod(access) && isInstanceMethod(access);
3836
}

retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/BackportLambdaInvocations.java

+11
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ private static void resetLambdaClassSequenceNumber() {
4848

4949
@Override
5050
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
51+
if (LambdaNaming.isBodyMethod(access, name)) {
52+
// Ensure our generated lambda class is able to call this method.
53+
access &= ~ACC_PRIVATE;
54+
}
5155
if (LambdaNaming.isDeserializationHook(access, name, desc)) {
5256
return null; // remove serialization hooks; we serialize lambda instances as-is
5357
}
@@ -62,6 +66,13 @@ Handle getLambdaAccessMethod(Handle implMethod) {
6266
// the method will be relocated to a companion class
6367
return implMethod;
6468
}
69+
if (LambdaNaming.isBodyMethodName(implMethod.getName())) {
70+
if (implMethod.getTag() == H_INVOKESPECIAL) {
71+
// The private body method is now package so switch its invocation from special to virtual.
72+
return new Handle(H_INVOKEVIRTUAL, implMethod.getOwner(), implMethod.getName(), implMethod.getDesc());
73+
}
74+
return implMethod;
75+
}
6576
// TODO: do not generate an access method if the impl method is not private (probably not implementable with a single pass)
6677
String name = "access$lambda$" + lambdaAccessToImplMethods.size();
6778
String desc = getLambdaAccessMethodDesc(implMethod);

retrolambda/src/main/java/net/orfjackal/retrolambda/lambdas/LambdaNaming.java

+8
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,12 @@ public static boolean isPlatformFactoryMethod(int access, String name, String de
3737
&& desc.equals(targetDesc)
3838
&& Flags.hasFlag(access, ACC_PRIVATE | ACC_STATIC);
3939
}
40+
41+
public static boolean isBodyMethodName(String name) {
42+
return name.startsWith("lambda\\$");
43+
}
44+
45+
public static boolean isBodyMethod(int access, String name) {
46+
return isBodyMethodName(name) && Flags.hasFlag(access, ACC_SYNTHETIC);
47+
}
4048
}

0 commit comments

Comments
 (0)