Skip to content

Skip access method when lambda body method can be promoted. #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package net.orfjackal.retrolambda.test;

import com.google.common.collect.ImmutableSet;
import org.junit.Test;

import java.lang.reflect.Method;
Expand Down Expand Up @@ -85,4 +86,24 @@ private NonCapturing() {
};
}
}


@Test
public void lambda_bodies_contain_no_unnecessary_methods() throws ClassNotFoundException {
Set<String> expected = ImmutableSet.of("lambda$main$0", "main");

Set<String> 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 = () -> {
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)) {
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}