Fix Inliner with nest
- private methods accessed with invokeVirtual/interface
should be inlined
- inlining should rewrite direct calls to virtual/
interface calls
- corresponding tests
Bug:133608609
Change-Id: I84c3b8c01ae899cf81fd4b611e7f72d382ff732a
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index a3a329e..6e702a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring.DexFieldWithAccess;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.ir.optimize.NestUtils;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
@@ -316,6 +317,10 @@
return accessFlags.isSynthetic();
}
+ public boolean isOnlyInlinedIntoNestMembers() {
+ return compilationState == PROCESSED_INLINING_CANDIDATE_SAME_NEST;
+ }
+
public boolean isInliningCandidate(
DexEncodedMethod container, Reason inliningReason, AppInfoWithSubtyping appInfo) {
checkIfObsolete();
@@ -347,7 +352,7 @@
case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
return containerType.isSamePackage(method.holder);
case PROCESSED_INLINING_CANDIDATE_SAME_NEST:
- return ConstraintWithTarget.sameNest(containerType, method.holder, appInfo);
+ return NestUtils.sameNest(containerType, method.holder, appInfo);
case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
return containerType == method.holder;
default:
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index ae432c6..d4529ea 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
+import com.android.tools.r8.ir.optimize.NestUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.ImmutableList;
@@ -417,8 +418,14 @@
Set<BasicBlock> blocksToRemove,
DexType downcast) {
assert blocksToRemove != null;
+ DexType codeHolder = code.method.method.holder;
+ DexType inlineeHolder = inlinee.method.method.holder;
+ if (codeHolder != inlineeHolder && inlinee.method.isOnlyInlinedIntoNestMembers()) {
+ // Should rewrite private calls to virtual calls.
+ assert NestUtils.sameNest(codeHolder, inlineeHolder, appView);
+ NestUtils.rewriteNestCallsForInlining(inlinee, codeHolder, appView);
+ }
boolean inlineeCanThrow = canThrow(inlinee);
-
// Split the block with the invocation into three blocks, where the first block contains all
// instructions before the invocation, the second block contains only the invocation, and the
// third block contains all instructions that follow the invocation.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index a7d3edb..8f3a6a7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -45,6 +45,10 @@
|| result == null;
}
+ public boolean isInterface() {
+ return itf;
+ }
+
@Override
public <T> T accept(InstructionVisitor<T> visitor) {
return visitor.visit(this);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 7c4b0aa..38cf994 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -88,7 +88,9 @@
AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView, this);
DexMethod method = getInvokedMethod();
- return appView.appInfo().lookupSingleInterfaceTarget(method, refinedReceiverType);
+ return appView
+ .appInfo()
+ .lookupSingleInterfaceTarget(method, invocationContext, refinedReceiverType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 2c1b5a6..eb0b6c7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -91,7 +91,9 @@
AppView<AppInfoWithLiveness> appView, DexType invocationContext) {
DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView, this);
DexMethod method = getInvokedMethod();
- return appView.appInfo().lookupSingleVirtualTarget(method, refinedReceiverType);
+ return appView
+ .appInfo()
+ .lookupSingleVirtualTarget(method, invocationContext, refinedReceiverType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 5d1ab42..0069dc6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassHierarchy;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -138,7 +137,7 @@
return true;
}
if (flags.isPrivate()) {
- return target == context;
+ return NestUtils.sameNest(target, context, appView);
}
if (flags.isProtected()) {
return appView.appInfo().isSubtype(context, target) || target.isSamePackage(context);
@@ -300,24 +299,6 @@
&& this.targetHolder == o.targetHolder;
}
- public static boolean sameNest(
- DexType contextHolder, DexType targetHolder, DexDefinitionSupplier definitions) {
- if (contextHolder == targetHolder) {
- return true;
- }
- DexClass contextHolderClass = definitions.definitionFor(contextHolder);
- assert contextHolderClass != null;
- if (!contextHolderClass.isInANest()) {
- return false;
- }
- DexClass targetHolderClass = definitions.definitionFor(targetHolder);
- if (targetHolderClass == null) {
- // Conservatively return false
- return false;
- }
- return contextHolderClass.getNestHost() == targetHolderClass.getNestHost();
- }
-
public static ConstraintWithTarget deriveConstraint(
DexType contextHolder, DexType targetHolder, AccessFlags flags, AppView<?> appView) {
if (flags.isPublic()) {
@@ -326,7 +307,7 @@
DexClass contextHolderClass = appView.definitionFor(contextHolder);
assert contextHolderClass != null;
if (contextHolderClass.isInANest()) {
- return sameNest(contextHolder, targetHolder, appView)
+ return NestUtils.sameNest(contextHolder, targetHolder, appView)
? new ConstraintWithTarget(Constraint.SAMENEST, targetHolder)
: NEVER;
}
@@ -389,7 +370,7 @@
return NEVER;
}
if (other.constraint == Constraint.SAMENEST) {
- if (sameNest(one.targetHolder, other.targetHolder, appView)) {
+ if (NestUtils.sameNest(one.targetHolder, other.targetHolder, appView)) {
return one;
}
return NEVER;
@@ -410,7 +391,7 @@
if (Constraint.SAMENEST.isSet(constraint)) {
assert one.constraint == Constraint.SAMENEST;
if (other.constraint == Constraint.SAMENEST) {
- if (sameNest(one.targetHolder, other.targetHolder, appView)) {
+ if (NestUtils.sameNest(one.targetHolder, other.targetHolder, appView)) {
return one;
}
return NEVER;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
new file mode 100644
index 0000000..bae9b27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2019, 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.ir.optimize;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+
+public class NestUtils {
+
+ public static boolean sameNest(DexType type1, DexType type2, DexDefinitionSupplier definitions) {
+ if (type1 == type2) {
+ return true;
+ }
+ DexClass clazz1 = definitions.definitionFor(type1);
+ if (clazz1 == null) {
+ // Conservatively return false
+ return false;
+ }
+ if (!clazz1.isInANest()) {
+ return false;
+ }
+ DexClass clazz2 = definitions.definitionFor(type2);
+ if (clazz2 == null) {
+ // Conservatively return false
+ return false;
+ }
+ return clazz1.getNestHost() == clazz2.getNestHost();
+ }
+
+ public static void rewriteNestCallsForInlining(
+ IRCode code, DexType callerHolder, AppView<?> appView) {
+ // This method is called when inlining code into the nest member callerHolder.
+ InstructionIterator iterator = code.instructionIterator();
+ DexClass callerHolderClass = appView.definitionFor(callerHolder);
+ assert callerHolderClass != null;
+ assert code.method.method.holder != callerHolder;
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (instruction.isInvokeDirect()) {
+ InvokeDirect invoke = instruction.asInvokeDirect();
+ DexMethod method = invoke.getInvokedMethod();
+ DexEncodedMethod encodedMethod = appView.definitionFor(method);
+ if (encodedMethod != null && !encodedMethod.isInstanceInitializer()) {
+ assert encodedMethod.isPrivateMethod();
+ // Call to private method which has now to be interface/virtual
+ // (Now call to nest member private method).
+ if (invoke.isInterface()) {
+ iterator.replaceCurrentInstruction(
+ new InvokeInterface(method, invoke.outValue(), invoke.inValues()));
+ } else {
+ iterator.replaceCurrentInstruction(
+ new InvokeVirtual(method, invoke.outValue(), invoke.inValues()));
+ }
+ }
+ } else if (instruction.isInvokeInterface() || instruction.isInvokeVirtual()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexMethod method = invoke.getInvokedMethod();
+ if (method.holder == callerHolder) {
+ DexEncodedMethod encodedMethod = appView.definitionFor(method);
+ if (encodedMethod != null && encodedMethod.isPrivateMethod()) {
+ // Interface/virtual nest member call to private method,
+ // which has now to be a direct call
+ // (Now call to same class private method).
+ iterator.replaceCurrentInstruction(
+ new InvokeDirect(
+ method, invoke.outValue(), invoke.inValues(), callerHolderClass.isInterface()));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 77ea68b..f48b72b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.optimize.NestUtils;
import com.android.tools.r8.utils.CollectionUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -769,9 +770,9 @@
}
switch (type) {
case VIRTUAL:
- return lookupSingleVirtualTarget(target);
+ return lookupSingleVirtualTarget(target, invocationContext);
case INTERFACE:
- return lookupSingleInterfaceTarget(target);
+ return lookupSingleInterfaceTarget(target, invocationContext);
case DIRECT:
return lookupDirectTarget(target);
case STATIC:
@@ -784,13 +785,18 @@
}
/** For mapping invoke virtual instruction to single target method. */
- public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method) {
+ public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method, DexType invocationContext) {
assert checkIfObsolete();
- return lookupSingleVirtualTarget(method, method.holder);
+ return lookupSingleVirtualTarget(method, invocationContext, method.holder);
}
- public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method, DexType refinedReceiverType) {
+ public DexEncodedMethod lookupSingleVirtualTarget(
+ DexMethod method, DexType invocationContext, DexType refinedReceiverType) {
assert checkIfObsolete();
+ DexEncodedMethod directResult = nestAccessLookup(method, invocationContext);
+ if (directResult != null) {
+ return directResult;
+ }
// This implements the logic from
// https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.invokevirtual
assert method != null;
@@ -847,6 +853,21 @@
return result;
}
+ private DexEncodedMethod nestAccessLookup(DexMethod method, DexType invocationContext) {
+ if (method.holder == invocationContext || !definitionFor(invocationContext).isInANest()) {
+ return null;
+ }
+ DexEncodedMethod directTarget = lookupDirectTarget(method);
+ assert directTarget == null || directTarget.method.holder == method.holder;
+ if (directTarget != null
+ && directTarget.isPrivateMethod()
+ && NestUtils.sameNest(method.holder, invocationContext, this)) {
+ return directTarget;
+ }
+
+ return null;
+ }
+
/**
* Computes which methods overriding <code>method</code> are visible for the subtypes of type.
*
@@ -937,14 +958,18 @@
return false;
}
- public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method) {
+ public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method, DexType invocationContext) {
assert checkIfObsolete();
- return lookupSingleInterfaceTarget(method, method.holder);
+ return lookupSingleInterfaceTarget(method, invocationContext, method.holder);
}
public DexEncodedMethod lookupSingleInterfaceTarget(
- DexMethod method, DexType refinedReceiverType) {
+ DexMethod method, DexType invocationContext, DexType refinedReceiverType) {
assert checkIfObsolete();
+ DexEncodedMethod directResult = nestAccessLookup(method, invocationContext);
+ if (directResult != null) {
+ return directResult;
+ }
if (instantiatedLambdas.contains(method.holder)) {
return null;
}
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java b/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java
new file mode 100644
index 0000000..2cbdf25
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestPvtFieldPropagated.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2019, 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 nesthostexample;
+
+public class NestPvtFieldPropagated {
+
+ public static class Inner {
+ private static String staticField = "toPropagateStatic";
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Inner.staticField);
+ }
+}
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
new file mode 100644
index 0000000..dd76928
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2019, 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 nesthostexample;
+
+public class NestPvtMethodCallInlined {
+
+ public static class Inner {
+
+ public String methodWithPvtCallToInline() {
+ return notInlinedPvtCall();
+ }
+
+ @NeverInline
+ private String notInlinedPvtCall() {
+ return "notInlinedPvtCallInner";
+ }
+
+ private String nestPvtCallToInline() {
+ return "nestPvtCallToInlineInner";
+ }
+ }
+
+ public interface InnerInterface {
+
+ default String methodWithPvtCallToInline() {
+ return notInlinedPvtCall();
+ }
+
+ @NeverInline
+ private String notInlinedPvtCall() {
+ return "notInlinedPvtCallInnerInterface";
+ }
+
+ private String nestPvtCallToInline() {
+ return "nestPvtCallToInlineInnerInterface";
+ }
+
+ default String dispatch(InnerSub sub) {
+ return sub.notInlinedPvtCall();
+ }
+
+ @NeverInline
+ default String dispatchInlining(InnerSub iSub) {
+ return iSub.dispatch(this);
+ }
+ }
+
+ public static class InnerInterfaceImpl implements InnerInterface {}
+
+ public static class InnerSub extends Inner {
+
+ @NeverInline
+ public String dispatchInlining(InnerInterface impl) {
+ return impl.dispatch(this);
+ }
+
+ public String dispatch(InnerInterface itf) {
+ return itf.notInlinedPvtCall();
+ }
+
+ @NeverInline
+ private String notInlinedPvtCall() {
+ return "notInlinedPvtCallInnerSub";
+ }
+
+ private String nestPvtCallToInline() {
+ return "nestPvtCallToInlineInnerSub";
+ }
+ }
+
+ public static void main(String[] args) {
+ Inner i = new Inner();
+ InnerSub iSub = new InnerSub();
+ InnerInterface impl = new InnerInterfaceImpl();
+
+ // Inlining through nest access (invoke virtual/interface).
+ System.out.println(i.nestPvtCallToInline());
+ System.out.println(impl.nestPvtCallToInline());
+
+ // Inlining transformations.
+ // Invoke direct -> invoke virtual.
+ System.out.println(i.methodWithPvtCallToInline());
+ // Invoke interface -> invoke virtual.
+ System.out.println(impl.methodWithPvtCallToInline());
+ // Invoke virtual -> invoke direct.
+ System.out.println(iSub.dispatchInlining(impl));
+ // Invoke interface -> invoke direct.
+ System.out.println(impl.dispatchInlining(iSub));
+
+ // Inheritance + invoke virtual and nest access.
+ // This may mess up lookup logic.
+ System.out.println(iSub.nestPvtCallToInline());
+ System.out.println(((Inner) iSub).nestPvtCallToInline());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
index ce661cb..77ae469 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
@@ -53,6 +53,13 @@
"NestHostInliningSubclasses$InnerNoPrivAccess",
"OutsideInliningNoAccess",
"OutsideInliningWithAccess",
+ "NestPvtMethodCallInlined",
+ "NestPvtMethodCallInlined$Inner",
+ "NestPvtMethodCallInlined$InnerInterface",
+ "NestPvtMethodCallInlined$InnerInterfaceImpl",
+ "NestPvtMethodCallInlined$InnerSub",
+ "NestPvtFieldPropagated",
+ "NestPvtFieldPropagated$Inner",
"NestHostExample",
"NestHostExample$NestMemberInner",
"NestHostExample$NestMemberInner$NestMemberInnerInner",
@@ -77,6 +84,8 @@
.put("prune", "BasicNestHostTreePruning")
.put("inlining", "NestHostInlining")
.put("inliningSub", "NestHostInliningSubclasses")
+ .put("pvtCallInlined", "NestPvtMethodCallInlined")
+ .put("memberPropagated", "NestPvtFieldPropagated")
.build();
public static final String ALL_RESULT_LINE =
String.join(
@@ -134,6 +143,18 @@
"staticInterfaceMethodstaticStaticInterfaceMethod",
"staticInterfaceMethodstaticStaticInterfaceMethod",
"staticInterfaceMethodstaticStaticInterfaceMethod"))
+ .put(
+ "pvtCallInlined",
+ StringUtils.lines(
+ "nestPvtCallToInlineInner",
+ "nestPvtCallToInlineInnerInterface",
+ "notInlinedPvtCallInner",
+ "notInlinedPvtCallInnerInterface",
+ "notInlinedPvtCallInnerSub",
+ "notInlinedPvtCallInnerInterface",
+ "nestPvtCallToInlineInnerSub",
+ "nestPvtCallToInlineInner"))
+ .put("memberPropagated", StringUtils.lines("toPropagateStatic"))
.build();
public static String getMainClass(String id) {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
new file mode 100644
index 0000000..5463fcc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2019, 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.nestaccesscontrol;
+
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NestMemberPropagatedTest extends TestBase {
+
+ public NestMemberPropagatedTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withAllApiLevels()
+ .build();
+ }
+
+ @Test
+ public void testPvtMemberPropagated() throws Exception {
+ List<Path> toCompile = classesMatching("NestPvtFieldPropagated");
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(getMainClass("memberPropagated"))
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.enableClassInlining = false;
+ })
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(toCompile)
+ .compile()
+ .inspect(this::assertMemberPropagated)
+ .run(parameters.getRuntime(), getMainClass("memberPropagated"))
+ .assertSuccessWithOutput(getExpectedResult("memberPropagated"));
+ }
+
+ private void assertMemberPropagated(CodeInspector inspector) {
+ for (FoundClassSubject subj : inspector.allClasses()) {
+ assertEquals(0, subj.allFields().size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
new file mode 100644
index 0000000..f861377
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2019, 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.nestaccesscontrol;
+
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
+import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NestMethodInlinedTest extends TestBase {
+
+ public NestMethodInlinedTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withAllApiLevels()
+ .build();
+ }
+
+ @Test
+ public void testPvtMethodCallInlined() throws Exception {
+ List<Path> toCompile = classesMatching("NestPvtMethodCallInlined");
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(getMainClass("pvtCallInlined"))
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.enableValuePropagation = false;
+ options.enableClassInlining = false;
+ options.enableVerticalClassMerging = false;
+ })
+ .enableInliningAnnotations("nesthostexample")
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(toCompile)
+ .compile()
+ .inspect(this::assertMethodsInlined)
+ .inspect(NestAttributesUpdateTest::assertNestAttributesCorrect)
+ .run(parameters.getRuntime(), getMainClass("pvtCallInlined"))
+ .assertSuccessWithOutput(getExpectedResult("pvtCallInlined"));
+ }
+
+ private void assertMethodsInlined(CodeInspector inspector) {
+ // Inlining through nest access.
+ int nbDispatchInlining = 0;
+ int nbNotInlinedPvtCall = 0;
+ for (FoundClassSubject subj : inspector.allClasses()) {
+ // TODO (b/133745203): inline invokeinterface accessed through nest access control.
+ // Remove the if.
+ if (!subj.getDexClass().isInterface()) {
+ assertTrue(
+ "nestPvtCallToInline should be inlined (from " + subj.getOriginalName() + ")",
+ subj.allMethods().stream()
+ .noneMatch(
+ method ->
+ method.toString().contains("nestPvtCallToInline")
+ || method.toString().contains("methodWithPvtCallToInline")));
+ }
+ // Inlining nest access should transform virtual/ift invokes -> direct.
+ MethodSubject methodSubject = subj.uniqueMethodWithName("dispatchInlining");
+ if (methodSubject.isPresent()) {
+ nbDispatchInlining++;
+ assertTrue(
+ methodSubject.streamInstructions().noneMatch(InstructionSubject::isInvokeVirtual));
+ // TODO (b/133745203): inline invokeinterface accessed through nest access control.
+ // Also assert no invokeInterface
+ }
+ methodSubject = subj.uniqueMethodWithName("notInlinedPvtCall");
+ if (methodSubject.isPresent()) {
+ nbNotInlinedPvtCall++;
+ }
+ }
+ assertEquals("dispatchInlining methods should not be inlined", 2, nbDispatchInlining);
+ assertEquals("notInlinedPvtCall methods should not be inlined", 3, nbNotInlinedPvtCall);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 70dd5ea..8e898e8 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -223,7 +223,7 @@
public void lookupSingleTarget() {
DexMethod method = buildMethod(invokeReceiver, methodName);
Assert.assertNotNull(appInfo.resolveMethod(toType(invokeReceiver), method).asResultOfResolve());
- DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method);
+ DexEncodedMethod singleVirtualTarget = appInfo.lookupSingleVirtualTarget(method, method.holder);
if (singleTargetHolderOrNull == null) {
Assert.assertNull(singleVirtualTarget);
} else {