Skip to content

Commit

Permalink
Skip access method when lambda body method can be promoted.
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeWharton committed Apr 27, 2016
1 parent 365b819 commit bf93245
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 5 deletions.
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);
}
}

0 comments on commit bf93245

Please sign in to comment.