Rewrite instance/static invokes to static/instance methods to ICCE in R8
Change-Id: If56ad9fa674cbc66f704b7b98ea4510f4f78a966
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index b425a2a..c1ff02b 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -84,6 +84,10 @@
return null;
}
+ public DexEncodedMethod getResolvedMethod() {
+ return null;
+ }
+
/** Short-hand to get the single resolution method if resolution finds it, null otherwise. */
public final DexEncodedMethod getSingleTarget() {
return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null;
@@ -189,6 +193,7 @@
return resolvedMethod;
}
+ @Override
public DexEncodedMethod getResolvedMethod() {
return resolvedMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 0b20c42..596f4cc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
+import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
@@ -57,6 +58,13 @@
AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
this.appView = appView;
this.apiLevelCompute = apiLevelCompute;
+ AlwaysThrowingInstructionDesugaring alwaysThrowingInstructionDesugaring =
+ appView.enableWholeProgramOptimizations()
+ ? new AlwaysThrowingInstructionDesugaring(appView.withClassHierarchy())
+ : null;
+ if (alwaysThrowingInstructionDesugaring != null) {
+ desugarings.add(alwaysThrowingInstructionDesugaring);
+ }
if (appView.options().desugarState.isOff()) {
this.nestBasedAccessDesugaring = null;
this.recordRewriter = null;
@@ -85,7 +93,9 @@
new InterfaceMethodRewriter(
appView,
SetUtils.newImmutableSetExcludingNullItems(
- backportedMethodRewriter, desugaredLibraryRetargeter));
+ alwaysThrowingInstructionDesugaring,
+ backportedMethodRewriter,
+ desugaredLibraryRetargeter));
desugarings.add(interfaceMethodRewriter);
} else {
interfaceMethodRewriter = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
new file mode 100644
index 0000000..5c89290
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -0,0 +1,200 @@
+// 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.ir.desugar.icce;
+
+import com.android.tools.r8.cf.code.CfConstNull;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class AlwaysThrowingInstructionDesugaring implements CfInstructionDesugaring {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+
+ public AlwaysThrowingInstructionDesugaring(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringCollection desugaringCollection,
+ DexItemFactory dexItemFactory) {
+ return computeDesugarDescription(instruction)
+ .desugarInstruction(
+ freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory);
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return computeDesugarDescription(instruction).needsDesugaring();
+ }
+
+ private DesugarDescription computeDesugarDescription(CfInstruction instruction) {
+ if (instruction.isInvoke()) {
+ CfInvoke invoke = instruction.asInvoke();
+ DexMethod invokedMethod = invoke.getMethod();
+ MethodResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(invokedMethod, invoke.isInterface());
+ if (shouldRewriteInvokeToThrow(invoke, resolutionResult)) {
+ return computeInvokeAsThrowRewrite(appView, invoke, resolutionResult);
+ }
+ }
+ return DesugarDescription.nothing();
+ }
+
+ private boolean shouldRewriteInvokeToThrow(
+ CfInvoke invoke, MethodResolutionResult resolutionResult) {
+ if (resolutionResult.isArrayCloneMethodResult()) {
+ return false;
+ }
+ if (resolutionResult.isFailedResolution()) {
+ // For now don't materialize NSMEs from failed resolutions.
+ return resolutionResult.asFailedResolution().hasMethodsCausingError();
+ }
+ assert resolutionResult.isSingleResolution();
+ return resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic();
+ }
+
+ public static DesugarDescription computeInvokeAsThrowRewrite(
+ AppView<?> appView, CfInvoke invoke, MethodResolutionResult resolutionResult) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory) ->
+ getThrowInstructions(
+ appView,
+ invoke,
+ resolutionResult,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext))
+ .build();
+ }
+
+ private static Collection<CfInstruction> getThrowInstructions(
+ AppView<?> appView,
+ CfInvoke invoke,
+ MethodResolutionResult resolutionResult,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext) {
+ MethodSynthesizerConsumer methodSynthesizerConsumer = null;
+ if (resolutionResult == null) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
+ } else if (resolutionResult.isSingleResolution()) {
+ if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
+ }
+ } else if (resolutionResult.isFailedResolution()) {
+ FailedResolutionResult failedResolutionResult = resolutionResult.asFailedResolution();
+ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+ if (failedResolutionResult.isIllegalAccessErrorResult(context.getHolder(), appInfo)) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowIllegalAccessErrorMethod;
+ } else if (failedResolutionResult.isNoSuchMethodErrorResult(context.getHolder(), appInfo)) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
+ } else if (failedResolutionResult.isIncompatibleClassChangeErrorResult()) {
+ methodSynthesizerConsumer =
+ UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
+ }
+ }
+
+ if (methodSynthesizerConsumer == null) {
+ assert false;
+ return null;
+ }
+
+ // Replace the entire effect of the invoke by by call to the throwing helper:
+ // ...
+ // invoke <method> [receiver] args*
+ // =>
+ // ...
+ // (pop arg)*
+ // [pop receiver]
+ // invoke <throwing-method>
+ // pop exception result
+ // [push fake result for <method>]
+ UtilityMethodForCodeOptimizations throwMethod =
+ methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
+ ProgramMethod throwProgramMethod = throwMethod.uncheckedGetMethod();
+ eventConsumer.acceptThrowMethod(throwProgramMethod, context);
+
+ ArrayList<CfInstruction> replacement = new ArrayList<>();
+ DexTypeList parameters = invoke.getMethod().getParameters();
+ for (int i = parameters.values.length - 1; i >= 0; i--) {
+ replacement.add(
+ new CfStackInstruction(
+ parameters.get(i).isWideType()
+ ? CfStackInstruction.Opcode.Pop2
+ : CfStackInstruction.Opcode.Pop));
+ }
+ if (!invoke.isInvokeStatic()) {
+ replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
+ }
+
+ CfInvoke throwInvoke =
+ new CfInvoke(
+ org.objectweb.asm.Opcodes.INVOKESTATIC, throwProgramMethod.getReference(), false);
+ assert throwInvoke.getMethod().getReturnType().isClassType();
+ replacement.add(throwInvoke);
+ replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
+
+ DexType returnType = invoke.getMethod().getReturnType();
+ if (!returnType.isVoidType()) {
+ replacement.add(
+ returnType.isPrimitiveType()
+ ? new CfConstNumber(0, ValueType.fromDexType(returnType))
+ : new CfConstNull());
+ } else {
+ // If the return type is void, the stack may need an extra slot to fit the return type of
+ // the call to the throwing method.
+ localStackAllocator.allocateLocalStack(1);
+ }
+ return replacement;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index d91a6fb..db52706 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -6,11 +6,8 @@
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
-import com.android.tools.r8.cf.code.CfConstNull;
-import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
@@ -27,13 +24,11 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -41,11 +36,9 @@
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
-import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
-import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
-import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -56,7 +49,6 @@
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
@@ -353,7 +345,7 @@
if (target != null && target.isDefaultMethod()) {
// Rewrite the invoke to a throw ICCE as the default method forward would otherwise hide the
// static / virtual mismatch.
- return computeInvokeAsThrowRewrite(invoke, resolution.asSingleResolution());
+ return computeInvokeAsThrowRewrite(invoke, resolution.asSingleResolution(), context);
}
return DesugarDescription.nothing();
}
@@ -447,7 +439,7 @@
.resolveMethodOnInterface(holder, invoke.getMethod())
.asSingleResolution();
if (holder.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, true)) {
- return computeInvokeAsThrowRewrite(invoke, resolutionResult);
+ return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
}
assert resolutionResult != null;
@@ -482,7 +474,7 @@
return computeInvokeDirect(holder, invoke, context);
}
if (resolution != null && resolution.getResolvedMethod().isStatic()) {
- return computeInvokeAsThrowRewrite(invoke, resolution);
+ return computeInvokeAsThrowRewrite(invoke, resolution, context);
}
DesugarDescription description = computeEmulatedInterfaceVirtualDispatchOrNull(invoke);
return description != null ? description : DesugarDescription.nothing();
@@ -527,7 +519,7 @@
MethodResolutionResult resolution =
appView.appInfoForDesugaring().resolveMethod(invokedMethod, invoke.isInterface());
if (resolution.isFailedResolution()) {
- return computeInvokeAsThrowRewrite(invoke, null);
+ return computeInvokeAsThrowRewrite(invoke, null, context);
}
SingleResolutionResult singleResolution = resolution.asSingleResolution();
@@ -606,23 +598,10 @@
}
private DesugarDescription computeInvokeAsThrowRewrite(
- CfInvoke invoke, SingleResolutionResult resolution) {
- return DesugarDescription.builder()
- .setDesugarRewrite(
- (freshLocalProvider,
- localStackAllocator,
- eventConsumer,
- context,
- methodProcessingContext,
- dexItemFactory) ->
- getThrowInstructions(
- invoke,
- resolution,
- localStackAllocator,
- eventConsumer,
- context,
- methodProcessingContext))
- .build();
+ CfInvoke invoke, SingleResolutionResult resolution, ProgramMethod context) {
+ assert !isAlreadyDesugared(invoke, context);
+ return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowRewrite(
+ appView, invoke, resolution);
}
private Collection<CfInstruction> getInvokeStaticInstructions(DexMethod newTarget) {
@@ -630,76 +609,6 @@
new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, newTarget, false));
}
- private Collection<CfInstruction> getThrowInstructions(
- CfInvoke invoke,
- SingleResolutionResult resolutionResult,
- LocalStackAllocator localStackAllocator,
- CfInstructionDesugaringEventConsumer eventConsumer,
- ProgramMethod context,
- MethodProcessingContext methodProcessingContext) {
- assert !isAlreadyDesugared(invoke, context);
-
- MethodSynthesizerConsumer methodSynthesizerConsumer;
- if (resolutionResult == null) {
- methodSynthesizerConsumer =
- UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod;
- } else if (resolutionResult.getResolvedMethod().isStatic() != invoke.isInvokeStatic()) {
- methodSynthesizerConsumer =
- UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod;
- } else {
- assert false;
- return null;
- }
-
- // Replace the entire effect of the invoke by by call to the throwing helper:
- // ...
- // invoke <method> [receiver] args*
- // =>
- // ...
- // (pop arg)*
- // [pop receiver]
- // invoke <throwing-method>
- // pop exception result
- // [push fake result for <method>]
- UtilityMethodForCodeOptimizations throwMethod =
- methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
- ProgramMethod throwProgramMethod = throwMethod.uncheckedGetMethod();
- eventConsumer.acceptThrowMethod(throwProgramMethod, context);
-
- ArrayList<CfInstruction> replacement = new ArrayList<>();
- DexTypeList parameters = invoke.getMethod().getParameters();
- for (int i = parameters.values.length - 1; i >= 0; i--) {
- replacement.add(
- new CfStackInstruction(
- parameters.get(i).isWideType()
- ? CfStackInstruction.Opcode.Pop2
- : CfStackInstruction.Opcode.Pop));
- }
- if (!invoke.isInvokeStatic()) {
- replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
- }
-
- CfInvoke throwInvoke =
- new CfInvoke(
- org.objectweb.asm.Opcodes.INVOKESTATIC, throwProgramMethod.getReference(), false);
- assert throwInvoke.getMethod().getReturnType().isClassType();
- replacement.add(throwInvoke);
- replacement.add(new CfStackInstruction(CfStackInstruction.Opcode.Pop));
-
- DexType returnType = invoke.getMethod().getReturnType();
- if (returnType != factory.voidType) {
- replacement.add(
- returnType.isPrimitiveType()
- ? new CfConstNumber(0, ValueType.fromDexType(returnType))
- : new CfConstNull());
- } else {
- // If the return type is void, the stack may need an extra slot to fit the return type of
- // the call to the throwing method.
- localStackAllocator.allocateLocalStack(1);
- }
- return replacement;
- }
-
private void leavingStaticInvokeToInterface(ProgramMethod method) {
// When leaving static interface method invokes possibly upgrade the class file
// version, but don't go above the initial class file version. If the input was
@@ -756,7 +665,7 @@
SingleResolutionResult resolutionResult =
appView.appInfoForDesugaring().resolveMethodOn(clazz, invokedMethod).asSingleResolution();
if (clazz.isInterface() && shouldRewriteToInvokeToThrow(resolutionResult, false)) {
- return computeInvokeAsThrowRewrite(invoke, resolutionResult);
+ return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
}
if (clazz.isInterface() && !clazz.isLibraryClass()) {
@@ -772,7 +681,7 @@
if (resolutionResult.getResolvedMethod().isPrivateMethod()) {
if (resolutionResult.isAccessibleFrom(context, appView.appInfoForDesugaring()).isFalse()) {
// TODO(b/145775365): This should throw IAE.
- return computeInvokeAsThrowRewrite(invoke, null);
+ return computeInvokeAsThrowRewrite(invoke, null, context);
}
return DesugarDescription.builder()
.setDesugarRewrite(
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index 6bf8cf9..51b4226 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -105,23 +105,22 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), mainClass);
- assertEquals(
- expectedOutput,
- result
- .getStdOut()
- .replace("java.lang.IncompatibleClassChangeError", "java.lang.IllegalAccessError"));
+ assertEquals(expectedOutput, result.getStdOut());
CodeInspector inspector = result.inspector();
+
+ MethodSignature barMethodSignatureAfterArgumentRemoval =
+ new MethodSignature(
+ "bar", STRING, enableArgumentRemoval ? ImmutableList.of() : ImmutableList.of("int"));
assertPublic(inspector, A.class, new MethodSignature("baz", STRING, ImmutableList.of()));
assertPublic(inspector, A.class, new MethodSignature("bar", STRING, ImmutableList.of()));
- assertPublic(inspector, A.class, new MethodSignature("bar", STRING, ImmutableList.of("int")));
+ assertPublic(inspector, A.class, barMethodSignatureAfterArgumentRemoval);
- MethodSignature blahMethodSignature =
+ MethodSignature blahMethodSignatureAfterArgumentRemoval =
new MethodSignature(
"blah", STRING, enableArgumentRemoval ? ImmutableList.of() : ImmutableList.of("int"));
- assertPublic(inspector, A.class, blahMethodSignature);
- assertPublic(inspector, B.class, blahMethodSignature);
- assertPublic(inspector, BB.class, blahMethodSignature);
+ assertPublic(inspector, A.class, blahMethodSignatureAfterArgumentRemoval);
+ assertPublic(inspector, BB.class, blahMethodSignatureAfterArgumentRemoval);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultTest.java b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultTest.java
index 9c9926d..698de1b 100644
--- a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultTest.java
@@ -63,12 +63,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- parameters.isCfRuntime()
- || parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M),
- r -> r.assertFailureWithErrorThatThrows(IllegalAccessError.class),
- // TODO(b/152199517): Should be illegal access for DEX.
- r -> r.assertSuccessWithOutputLines("I::foo"));
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
}
@NoVerticalClassMerging
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress199142666.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress199142666.java
index e92f84a..7f77799 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress199142666.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress199142666.java
@@ -4,13 +4,13 @@
package com.android.tools.r8.ir.optimize.inliner;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.io.IOException;
@@ -39,24 +39,21 @@
@Test
public void testInliningWhenInvalidCaller() throws Exception {
- R8TestRunResult run =
- testForR8(parameters.getBackend())
- .addProgramClasses(HasInvalidStaticCall.class)
- .addProgramClassFileData(getVirtualAAsStaticA())
- .addKeepMainRule(HasInvalidStaticCall.class)
- .addKeepMethodRules(StaticA.class, "void foo()")
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), HasInvalidStaticCall.class);
- if (parameters.getRuntime().asDex().getVm().getVersion().isDalvik()) {
- // TODO(b/199142666): We should not inline to provoke this error.
- run.assertFailureWithErrorThatMatches(
- containsString("invoke type does not match method type of"));
- } else {
- // TODO(b/199142666): We should consider if we want to inline in this case (there are no
- // verification errors)
- run.assertSuccessWithOutputLines("foochanged")
- .inspect(inspector -> ensureThisNumberOfCalls(inspector, HasInvalidStaticCall.class, 2));
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(HasInvalidStaticCall.class)
+ .addProgramClassFileData(getVirtualAAsStaticA())
+ .addKeepMainRule(HasInvalidStaticCall.class)
+ .addKeepMethodRules(StaticA.class, "void foo()")
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), HasInvalidStaticCall.class)
+ // TODO(b/199142666): We should consider if we want to inline in this case (there are no
+ // verification errors)
+ .assertSuccessWithOutputLines("foochanged")
+ .inspect(
+ inspector -> {
+ ensureThisNumberOfCalls(inspector, HasInvalidStaticCall.class, 1);
+ ensureThisNumberThrowICCE(inspector, HasInvalidStaticCall.class, 1);
+ });
}
@Test
@@ -70,10 +67,13 @@
.run(parameters.getRuntime(), TargetHasInvalidStaticCall.class)
.assertSuccessWithEmptyOutput()
.inspect(
- inspector -> ensureThisNumberOfCalls(inspector, TargetHasInvalidStaticCall.class, 1));
+ inspector -> {
+ ensureThisNumberOfCalls(inspector, TargetHasInvalidStaticCall.class, 0);
+ ensureThisNumberThrowICCE(inspector, TargetHasInvalidStaticCall.class, 2);
+ });
}
- private void ensureThisNumberOfCalls(CodeInspector inspector, Class clazz, int fooCalls) {
+ private void ensureThisNumberOfCalls(CodeInspector inspector, Class<?> clazz, int fooCalls) {
long count =
inspector
.clazz(clazz)
@@ -85,6 +85,27 @@
assertEquals(fooCalls, count);
}
+ private void ensureThisNumberThrowICCE(CodeInspector inspector, Class<?> clazz, int expected) {
+ IRCode code = inspector.clazz(clazz).mainMethod().buildIR();
+ long count =
+ code.streamInstructions()
+ .filter(Instruction::isThrow)
+ .filter(
+ instruction ->
+ instruction
+ .getFirstOperand()
+ .isDefinedByInstructionSatisfying(
+ definition ->
+ definition.isNewInstance()
+ && definition
+ .asNewInstance()
+ .getType()
+ .getTypeName()
+ .equals(IncompatibleClassChangeError.class.getTypeName())))
+ .count();
+ assertEquals(expected, count);
+ }
+
static class StaticA {
public static void callFoo() {
StaticA.foo();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java
index 1d188fb..a689715 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java
@@ -204,8 +204,7 @@
compileResult
.run(parameters.getRuntime(), swappingMain)
- .assertFailureWithErrorThatThrows(IllegalAccessError.class)
- .assertFailureWithErrorThatMatches(containsString(getMethodSignature("X", "y")));
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
CodeInspector codeInspector = compileResult.inspector();
ClassSubject base = codeInspector.clazz("A");
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 9c596c9..6289967 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -186,7 +186,7 @@
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, false));
}
@Test
@@ -197,10 +197,10 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, true));
}
- private void checkExpectedResult(TestRunResult<?> result) {
+ private void checkExpectedResult(TestRunResult<?> result, boolean isR8) {
// If not in the same nest, the error is always illegal access.
if (!inSameNest) {
result.assertFailureWithErrorThatThrows(IllegalAccessError.class);
@@ -209,8 +209,8 @@
// If in the same nest but the reference is not exact, the error is always no such method.
if (!symbolicReferenceIsDefiningType) {
- // TODO(b/145775365): D8/R8 does not preserve the thrown error.
- if (parameters.isDexRuntime()) {
+ // TODO(b/145775365): D8 does not preserve the thrown error.
+ if (parameters.isDexRuntime() && !isR8) {
result.assertFailureWithErrorThatThrows(IllegalAccessError.class);
return;
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 9a549f2..795f109 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -82,7 +82,7 @@
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, false));
}
@Test
@@ -93,11 +93,11 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, true));
}
- private void checkExpectedResult(TestRunResult<?> result) {
- if (inSameNest && parameters.isCfRuntime()) {
+ private void checkExpectedResult(TestRunResult<?> result, boolean isR8) {
+ if (inSameNest && (parameters.isCfRuntime() || isR8)) {
result.assertFailureWithErrorThatMatches(containsString(NoSuchMethodError.class.getName()));
} else {
result.assertFailureWithErrorThatMatches(containsString(IllegalAccessError.class.getName()));
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index ec1222f..9adae80 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -81,7 +81,7 @@
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, false));
}
@Test
@@ -92,11 +92,11 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkExpectedResult);
+ .apply(runResult -> checkExpectedResult(runResult, true));
}
- private void checkExpectedResult(TestRunResult<?> result) {
- if (inSameNest && parameters.isCfRuntime()) {
+ private void checkExpectedResult(TestRunResult<?> result, boolean isR8) {
+ if (inSameNest && (parameters.isCfRuntime() || isR8)) {
result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
} else {
result.assertFailureWithErrorThatThrows(IllegalAccessError.class);
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index f529432..7c9c834 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -105,9 +105,9 @@
"In C.m3()",
"In A.m4()",
"In A.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "In A.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
+ "Caught IncompatibleClassChangeError when calling B.m3()",
"In C.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "In C.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
+ "Caught IncompatibleClassChangeError when calling B.m3()",
"In C.m1()",
"In C.m3()",
"");
@@ -179,7 +179,7 @@
assertThat(classSubject, isPresentAndRenamed());
assertThat(classSubject.method("void", "m1", ImmutableList.of()), isPresent());
assertThat(classSubject.method("void", "m2", ImmutableList.of()), isAbsent());
- assertThat(classSubject.method("void", "m3", ImmutableList.of()), isPresent());
+ assertThat(classSubject.method("void", "m3", ImmutableList.of()), isAbsent());
assertThat(classSubject.method("void", "m4", ImmutableList.of()), isAbsent());
}
}