Track exception guard types to prevent merging.
Bug: 163311975
Change-Id: I4f77dc23f09d402dbcb957d60ce074777378af79
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index 8e9752b..01ff28f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.graph.CfCompareHelper;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -54,4 +56,8 @@
.thenComparing(c -> c.targets, ComparatorUtils.listComparator(helper::compareLabels))
.compare(this, other);
}
+
+ public void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+ guards.forEach(registry::registerExceptionGuard);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 141484c..a08d65e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -508,7 +508,7 @@
for (CfInstruction instruction : instructions) {
instruction.registerUse(registry, method);
}
- tryCatchRanges.forEach(tryCatch -> tryCatch.guards.forEach(registry::registerTypeReference));
+ tryCatchRanges.forEach(tryCatch -> tryCatch.internalRegisterUse(registry, method));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 033182e..4a3327a 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -75,6 +75,10 @@
registerTypeReference(type);
}
+ public void registerExceptionGuard(DexType guard) {
+ registerTypeReference(guard);
+ }
+
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
switch (methodHandle.type) {
case INSTANCE_GET:
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
new file mode 100644
index 0000000..1bf392e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.graph.analysis;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnqueuerExceptionGuardAnalysis {
+ void traceExceptionGuard(DexType guard, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
index dff9967..23f10b2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
@@ -18,7 +18,6 @@
@Override
public boolean canMerge(DexProgramClass clazz) {
// We currently assume we only merge classes that implement the same set of interfaces.
- return !(this.classMergingEnqueuerExtension.isCheckCastType(clazz)
- || this.classMergingEnqueuerExtension.isInstanceOfType(clazz));
+ return !classMergingEnqueuerExtension.isRuntimeCheckType(clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java b/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
index a6d7326..6a2151b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
@@ -9,15 +9,19 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
import com.google.common.collect.Sets;
import java.util.Set;
public class ClassMergingEnqueuerExtension
- implements EnqueuerInstanceOfAnalysis, EnqueuerCheckCastAnalysis {
+ implements EnqueuerInstanceOfAnalysis,
+ EnqueuerCheckCastAnalysis,
+ EnqueuerExceptionGuardAnalysis {
private final Set<DexType> instanceOfTypes = Sets.newIdentityHashSet();
private final Set<DexType> checkCastTypes = Sets.newIdentityHashSet();
+ private final Set<DexType> exceptionGuardTypes = Sets.newIdentityHashSet();
private final DexItemFactory factory;
public ClassMergingEnqueuerExtension(DexItemFactory factory) {
@@ -34,6 +38,11 @@
instanceOfTypes.add(type.toBaseType(factory));
}
+ @Override
+ public void traceExceptionGuard(DexType guard, ProgramMethod context) {
+ exceptionGuardTypes.add(guard);
+ }
+
public boolean isCheckCastType(DexProgramClass clazz) {
return checkCastTypes.contains(clazz.type);
}
@@ -42,7 +51,18 @@
return instanceOfTypes.contains(clazz.type);
}
+ public boolean isExceptionGuardType(DexProgramClass clazz) {
+ return exceptionGuardTypes.contains(clazz.type);
+ }
+
+ public boolean isRuntimeCheckType(DexProgramClass clazz) {
+ return isInstanceOfType(clazz) || isCheckCastType(clazz) || isExceptionGuardType(clazz);
+ }
+
public void attach(Enqueuer enqueuer) {
- enqueuer.registerInstanceOfAnalysis(this).registerCheckCastAnalysis(this);
+ enqueuer
+ .registerInstanceOfAnalysis(this)
+ .registerCheckCastAnalysis(this)
+ .registerExceptionGuardAnalysis(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 34fb593..b67d90d 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -134,6 +134,11 @@
}
@Override
+ public void registerExceptionGuard(DexType guard) {
+ enqueuer.traceExceptionGuard(guard, context);
+ }
+
+ @Override
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
super.registerMethodHandle(methodHandle, use);
enqueuer.traceMethodHandle(methodHandle, use, context);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 21bf839..230e5ca 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -65,6 +65,7 @@
import com.android.tools.r8.graph.analysis.DesugaredLibraryConversionWrapperAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
import com.android.tools.r8.ir.analysis.proto.ProtoEnqueuerUseRegistry;
@@ -178,6 +179,7 @@
private Set<EnqueuerAnalysis> analyses = Sets.newIdentityHashSet();
private Set<EnqueuerInvokeAnalysis> invokeAnalyses = Sets.newIdentityHashSet();
private Set<EnqueuerInstanceOfAnalysis> instanceOfAnalyses = Sets.newIdentityHashSet();
+ private Set<EnqueuerExceptionGuardAnalysis> exceptionGuardAnalyses = Sets.newIdentityHashSet();
private Set<EnqueuerCheckCastAnalysis> checkCastAnalyses = Sets.newIdentityHashSet();
// Don't hold a direct pointer to app info (use appView).
@@ -436,6 +438,11 @@
return this;
}
+ public Enqueuer registerExceptionGuardAnalysis(EnqueuerExceptionGuardAnalysis analysis) {
+ exceptionGuardAnalyses.add(analysis);
+ return this;
+ }
+
public void setAnnotationRemoverBuilder(AnnotationRemover.Builder annotationRemoverBuilder) {
this.annotationRemoverBuilder = annotationRemoverBuilder;
}
@@ -1027,6 +1034,11 @@
traceTypeReference(type, currentMethod);
}
+ void traceExceptionGuard(DexType guard, ProgramMethod currentMethod) {
+ exceptionGuardAnalyses.forEach(analysis -> analysis.traceExceptionGuard(guard, currentMethod));
+ traceTypeReference(guard, currentMethod);
+ }
+
void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
new file mode 100644
index 0000000..c751735
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2020, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class DistinguishExceptionClassesTest extends HorizontalClassMergingTestBase {
+ public DistinguishExceptionClassesTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("test success")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Exception1.class), isPresent());
+ assertThat(codeInspector.clazz(Exception2.class), isPresent());
+ });
+ }
+
+ public static class Exception1 extends Exception {}
+
+ public static class Exception2 extends Exception {}
+
+ public static class Main {
+ public static void main(String[] args) {
+ try {
+ try {
+ if (System.currentTimeMillis() > 0) {
+ throw new Exception2();
+ } else {
+ throw new Exception1();
+ }
+ } catch (Exception1 ex) {
+ System.out.println("test failed");
+ }
+ } catch (Exception2 ex) {
+ System.out.println("test success");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
index 167c6c1..ababe2f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
@@ -38,7 +38,6 @@
@Test
public void testClassesHaveBeenMerged() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(ExceptionTablesTest.class)
.addKeepMainRule(TestClass.class)