Make LambdaClass.Target classes static inner classes.
Change-Id: I1eb0c5f2e897ea70820f364a1829472f9994a404
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index f07a802..8236617 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -4,20 +4,25 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
// Source code representing synthesized accessor method.
public class AccessorMethodSourceCode {
- public static CfCode build(LambdaClass lambda, DexMethod accessor) {
- DexMethod target = lambda.descriptor.implHandle.asMethod();
+ public static CfCode build(
+ DexMethod target,
+ boolean isInterface,
+ MethodHandleType type,
+ DexMethod accessor,
+ AppView<?> appView) {
ForwardMethodBuilder forwardMethodBuilder =
- ForwardMethodBuilder.builder(lambda.appView.dexItemFactory()).setStaticSource(accessor);
- boolean isInterface = lambda.descriptor.implHandle.isInterface;
- switch (lambda.descriptor.implHandle.type) {
+ ForwardMethodBuilder.builder(appView.dexItemFactory()).setStaticSource(accessor);
+ switch (type) {
case INVOKE_INSTANCE:
{
forwardMethodBuilder.setVirtualTarget(target, isInterface);
@@ -35,7 +40,7 @@
}
case INVOKE_CONSTRUCTOR:
{
- forwardMethodBuilder.setConstructorTarget(target, lambda.appView.dexItemFactory());
+ forwardMethodBuilder.setConstructorTarget(target, appView.dexItemFactory());
break;
}
case INVOKE_INTERFACE:
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index c2d95ca..afedd24 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -312,14 +312,18 @@
appView.appInfoForDesugaring().resolveMethod(implMethod, implHandle.isInterface);
if (resolution.isFailedResolution()) {
return new InvalidLambdaImplTarget(
- implMethod, Type.STATIC, appView.dexItemFactory().icceType);
+ implMethod,
+ Type.STATIC,
+ appView.dexItemFactory().icceType,
+ descriptor.implHandle.isInterface);
}
SingleResolutionResult result = resolution.asSingleResolution();
assert result.getResolvedMethod().isStatic();
assert result.getResolvedHolder().isProgramClass();
return new StaticLambdaImplTarget(
new ProgramMethod(
- result.getResolvedHolder().asProgramClass(), result.getResolvedMethod()));
+ result.getResolvedHolder().asProgramClass(), result.getResolvedMethod()),
+ descriptor.implHandle.isInterface);
}
assert implHandle.type.isInvokeDirect();
@@ -334,18 +338,24 @@
DexProto newProto = appView.dexItemFactory().createProto(implProto.returnType, newParams);
return new InterfaceLambdaImplTarget(
- appView.dexItemFactory().createMethod(implMethod.holder, newProto, implMethod.name));
+ descriptor.implHandle.asMethod(),
+ descriptor.implHandle.isInterface,
+ appView.dexItemFactory().createMethod(implMethod.holder, newProto, implMethod.name),
+ appView);
} else {
// Otherwise we need to ensure the method can be reached publicly by virtual dispatch.
// To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual
// we add the fully qualified method-holder name as suffix to the lambda-method name.
return new InstanceLambdaImplTarget(
+ descriptor.implHandle.asMethod(),
+ descriptor.implHandle.isInterface,
appView
.dexItemFactory()
.createMethod(
implMethod.holder,
implMethod.proto,
- appendFullyQualifiedHolderToMethodName(implMethod, appView.dexItemFactory())));
+ appendFullyQualifiedHolderToMethodName(implMethod, appView.dexItemFactory())),
+ appView);
}
}
@@ -356,7 +366,8 @@
descriptor.implHandle.type.isInvokeDirect();
if (doesNotNeedAccessor(accessedFrom)) {
- return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL);
+ return new NoAccessorMethodTarget(
+ descriptor.implHandle.asMethod(), Type.VIRTUAL, descriptor.implHandle.isInterface);
}
// We need to generate an accessor method in `accessedFrom` class/interface
// for accessing the original instance impl-method. Note that impl-method's
@@ -379,7 +390,12 @@
.createMethod(
accessedFrom.getHolderType(), accessorProto, generateUniqueLambdaMethodName());
- return new ClassMethodWithAccessorTarget(accessorMethod);
+ return new ClassMethodWithAccessorTarget(
+ descriptor.implHandle.asMethod(),
+ descriptor.implHandle.isInterface,
+ descriptor.implHandle.type,
+ accessorMethod,
+ appView);
}
// Create targets for static method referenced directly without
@@ -388,7 +404,8 @@
assert descriptor.implHandle.type.isInvokeStatic();
if (doesNotNeedAccessor(accessedFrom)) {
- return new NoAccessorMethodTarget(Invoke.Type.STATIC);
+ return new NoAccessorMethodTarget(
+ descriptor.implHandle.asMethod(), Type.STATIC, descriptor.implHandle.isInterface);
}
// We need to generate an accessor method in `accessedFrom` class/interface
@@ -401,7 +418,12 @@
accessedFrom.getHolderType(),
descriptor.implHandle.asMethod().proto,
generateUniqueLambdaMethodName());
- return new ClassMethodWithAccessorTarget(accessorMethod);
+ return new ClassMethodWithAccessorTarget(
+ descriptor.implHandle.asMethod(),
+ descriptor.implHandle.isInterface,
+ descriptor.implHandle.type,
+ accessorMethod,
+ appView);
}
// Create targets for constructor referenced directly without lambda$ methods.
@@ -412,7 +434,8 @@
assert implHandle.type.isInvokeConstructor();
if (doesNotNeedAccessor(accessedFrom)) {
- return new NoAccessorMethodTarget(Invoke.Type.DIRECT);
+ return new NoAccessorMethodTarget(
+ descriptor.implHandle.asMethod(), Type.DIRECT, descriptor.implHandle.isInterface);
}
// We need to generate an accessor method in `accessedFrom` class/interface for
@@ -428,14 +451,20 @@
.dexItemFactory()
.createMethod(
accessedFrom.getHolderType(), accessorProto, generateUniqueLambdaMethodName());
- return new ClassMethodWithAccessorTarget(accessorMethod);
+ return new ClassMethodWithAccessorTarget(
+ descriptor.implHandle.asMethod(),
+ descriptor.implHandle.isInterface,
+ descriptor.implHandle.type,
+ accessorMethod,
+ appView);
}
// Create targets for interface methods.
private Target createInterfaceMethodTarget(ProgramMethod accessedFrom) {
assert descriptor.implHandle.type.isInvokeInterface();
assert doesNotNeedAccessor(accessedFrom);
- return new NoAccessorMethodTarget(Invoke.Type.INTERFACE);
+ return new NoAccessorMethodTarget(
+ descriptor.implHandle.asMethod(), Type.INTERFACE, descriptor.implHandle.isInterface);
}
private DexString generateUniqueLambdaMethodName() {
@@ -447,19 +476,20 @@
// Represents information about the method lambda class need to delegate the call to. It may
// be the same method as specified in lambda descriptor or a newly synthesized accessor.
// Also provides action for ensuring accessibility of the referenced symbols.
- public abstract class Target {
+ public abstract static class Target {
final DexMethod callTarget;
final Invoke.Type invokeType;
+ final boolean isInterface;
private boolean hasEnsuredAccessibility;
- private ProgramMethod accessibilityBridge;
- Target(DexMethod callTarget, Invoke.Type invokeType) {
+ Target(DexMethod callTarget, Type invokeType, boolean isInterface) {
assert callTarget != null;
assert invokeType != null;
this.callTarget = callTarget;
this.invokeType = invokeType;
+ this.isInterface = isInterface;
}
// Ensure access of the referenced symbol(s).
@@ -476,29 +506,27 @@
ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
Consumer<ProgramMethod> needsProcessingConsumer) {
if (!hasEnsuredAccessibility) {
- accessibilityBridge =
- ensureAccessibility(forcefullyMovedLambdaMethodConsumer, needsProcessingConsumer);
+ ensureAccessibility(forcefullyMovedLambdaMethodConsumer, needsProcessingConsumer);
hasEnsuredAccessibility = true;
}
}
boolean isInterface() {
- return descriptor.implHandle.isInterface;
+ return isInterface;
}
}
- public abstract class D8SpecificTarget extends Target {
- D8SpecificTarget(DexMethod callTarget, Type invokeType) {
- super(callTarget, invokeType);
- assert !appView.enableWholeProgramOptimizations();
+ public abstract static class D8SpecificTarget extends Target {
+ D8SpecificTarget(DexMethod callTarget, Type invokeType, boolean isInterface) {
+ super(callTarget, invokeType, isInterface);
}
}
// Used for targeting methods referenced directly without creating accessors.
- private final class NoAccessorMethodTarget extends Target {
+ private static final class NoAccessorMethodTarget extends Target {
- NoAccessorMethodTarget(Invoke.Type invokeType) {
- super(descriptor.implHandle.asMethod(), invokeType);
+ NoAccessorMethodTarget(DexMethod method, Type invokeType, boolean isInterface) {
+ super(method, invokeType, isInterface);
}
@Override
@@ -510,12 +538,12 @@
}
// Used for static private lambda$ methods. Only needs access relaxation.
- private final class StaticLambdaImplTarget extends D8SpecificTarget {
+ private static final class StaticLambdaImplTarget extends D8SpecificTarget {
final ProgramMethod target;
- StaticLambdaImplTarget(ProgramMethod target) {
- super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC);
+ StaticLambdaImplTarget(ProgramMethod target, boolean isInterface) {
+ super(target.getReference(), Invoke.Type.STATIC, isInterface);
this.target = target;
}
@@ -535,10 +563,16 @@
// Used for instance private lambda$ methods on interfaces which need to be converted to public
// static methods. They can't remain instance methods as they will end up on the companion class.
- private class InterfaceLambdaImplTarget extends D8SpecificTarget {
+ private static final class InterfaceLambdaImplTarget extends D8SpecificTarget {
- InterfaceLambdaImplTarget(DexMethod staticMethod) {
- super(staticMethod, Type.STATIC);
+ private final AppView<?> appView;
+ private final DexMethod implMethod;
+
+ InterfaceLambdaImplTarget(
+ DexMethod implMethod, boolean isInterface, DexMethod staticMethod, AppView<?> appView) {
+ super(staticMethod, Type.STATIC, isInterface);
+ this.implMethod = implMethod;
+ this.appView = appView;
}
@Override
@@ -547,7 +581,6 @@
Consumer<ProgramMethod> needsProcessingConsumer) {
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
- DexMethod implMethod = descriptor.implHandle.asMethod();
DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
DexEncodedMethod replacement =
@@ -603,12 +636,13 @@
}
}
- class InvalidLambdaImplTarget extends Target {
+ static final class InvalidLambdaImplTarget extends Target {
final DexType exceptionType;
- public InvalidLambdaImplTarget(DexMethod callTarget, Type invokeType, DexType exceptionType) {
- super(callTarget, invokeType);
+ public InvalidLambdaImplTarget(
+ DexMethod callTarget, Type invokeType, DexType exceptionType, boolean isInterface) {
+ super(callTarget, invokeType, isInterface);
this.exceptionType = exceptionType;
}
@@ -621,10 +655,16 @@
}
// Used for instance private lambda$ methods which need to be converted to public methods.
- private class InstanceLambdaImplTarget extends D8SpecificTarget {
+ private static final class InstanceLambdaImplTarget extends D8SpecificTarget {
- InstanceLambdaImplTarget(DexMethod staticMethod) {
- super(staticMethod, Type.VIRTUAL);
+ private final DexMethod implMethod;
+ private final AppView<?> appView;
+
+ InstanceLambdaImplTarget(
+ DexMethod implMethod, boolean isInterface, DexMethod staticMethod, AppView<?> appView) {
+ super(staticMethod, Type.VIRTUAL, isInterface);
+ this.implMethod = implMethod;
+ this.appView = appView;
}
@Override
@@ -634,7 +674,6 @@
// When compiling with whole program optimization, check that we are not inplace modifying.
// For all instantiation points for which the compiler creates lambda$
// methods, it creates these methods in the same class/interface.
- DexMethod implMethod = descriptor.implHandle.asMethod();
DexProgramClass implMethodHolder = appView.definitionFor(implMethod.holder).asProgramClass();
DexEncodedMethod replacement =
@@ -687,12 +726,25 @@
// Used for instance/static methods or constructors accessed via
// synthesized accessor method. Needs accessor method to be created.
- private class ClassMethodWithAccessorTarget extends Target {
+ private static class ClassMethodWithAccessorTarget extends Target {
- ClassMethodWithAccessorTarget(DexMethod accessorMethod) {
- super(accessorMethod, Invoke.Type.STATIC);
+ private final AppView<?> appView;
+ private final DexMethod implMethod;
+ private final MethodHandleType type;
+
+ ClassMethodWithAccessorTarget(
+ DexMethod implMethod,
+ boolean isInterface,
+ MethodHandleType type,
+ DexMethod accessorMethod,
+ AppView<?> appView) {
+ super(accessorMethod, Invoke.Type.STATIC, isInterface);
+ this.appView = appView;
+ this.implMethod = implMethod;
+ this.type = type;
}
+
@Override
ProgramMethod ensureAccessibility(
ForcefullyMovedLambdaMethodConsumer forcefullyMovedLambdaMethodConsumer,
@@ -718,7 +770,9 @@
DexEncodedMethod.syntheticBuilder()
.setMethod(callTarget)
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(AccessorMethodSourceCode.build(LambdaClass.this, callTarget))
+ .setCode(
+ AccessorMethodSourceCode.build(
+ implMethod, isInterface, type, callTarget, appView))
// The api level is computed when tracing.
.disableAndroidApiLevelCheck()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateStaticMethodTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateStaticMethodTest.java
new file mode 100644
index 0000000..448234c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaPrivateStaticMethodTest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.lambdas;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LambdaPrivateStaticMethodTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public LambdaPrivateStaticMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ interface MyFun {
+ void run();
+ }
+
+ static class TestClass {
+
+ public static void run(MyFun fn) {
+ fn.run();
+ }
+
+ public static void main(String[] args) {
+ run(() -> System.out.println("Hello world"));
+ }
+ }
+}