Version 2.1.42
Cherry-pick: Use compat mode for shrinking the desugared library
CL: https://r8-review.googlesource.com/c/r8/+/52143
Cherry-pick: Collect desugared library types used in check-cast and instanceof
CL: https://r8-review.googlesource.com/c/r8/+/52140
Cherry-pick: Collect desugared library exception types used in try/catch
CL: https://r8-review.googlesource.com/c/r8/+/52124
Cherry-pick: Provide access to try/catch guards in code inspector
CL: https://r8-review.googlesource.com/c/r8/+/52123
Cherry-pick: Add reproduction of duplicate methods due to staticizer
CL: https://r8-review.googlesource.com/52125
Cherry-pick: Disable moving not processed members to host for
staticizer
CL: https://r8-review.googlesource.com/52142
Bug: 158815562
Bug: 158417777
Bug: 157966650
Bug: 158018192
Change-Id: I32cdfc36dd24db48deb4b3e692bbbbb68d89a28e
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index b1072df..93d688d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -291,7 +291,7 @@
if (isShrinking()) {
R8Command.Builder r8Builder =
- R8Command.builder(getReporter())
+ new CompatProguardCommandBuilder(true, getReporter())
.addProgramResourceProvider(desugaredLibrary)
.setSynthesizedClassesPrefix(
libraryConfiguration.getSynthesizedLibraryClassesPackagePrefix())
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 04e1817..b315555 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.1.41";
+ public static final String LABEL = "2.1.42";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 57151fa..f472800 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -38,6 +38,11 @@
}
@Override
+ public CheckCast asCheckCast() {
+ return this;
+ }
+
+ @Override
public boolean isCheckCast() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java
index 11b2d5c..4550406 100644
--- a/src/main/java/com/android/tools/r8/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -23,6 +23,16 @@
}
@Override
+ public InstanceOf asInstanceOf() {
+ return this;
+ }
+
+ @Override
+ public boolean isInstanceOf() {
+ return true;
+ }
+
+ @Override
public String getName() {
return NAME;
}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index b6b1f9c..86abac6 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -127,10 +127,22 @@
this.offset = offset;
}
+ public CheckCast asCheckCast() {
+ return null;
+ }
+
public boolean isCheckCast() {
return false;
}
+ public InstanceOf asInstanceOf() {
+ return null;
+ }
+
+ public boolean isInstanceOf() {
+ return false;
+ }
+
public ConstString asConstString() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
index ef1b81b..037d769 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -112,6 +112,10 @@
desugaredLibraryCodeToKeep.recordMethod(method);
} else if (insn.isConstClass()) {
desugaredLibraryCodeToKeep.recordClass(insn.asConstClass().getType());
+ } else if (insn.isInstanceOf()) {
+ desugaredLibraryCodeToKeep.recordClass(insn.asInstanceOf().getType());
+ } else if (insn.isCheckCast()) {
+ desugaredLibraryCodeToKeep.recordClass(insn.asCheckCast().getType());
}
insn.write(shortBuffer, mapping);
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 867f32d..0047bf8 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -519,6 +519,7 @@
for (TypeAddrPair pair : handler.pairs) {
dest.putUleb128(mapping.getOffsetFor(pair.type));
dest.putUleb128(pair.addr);
+ desugaredLibraryCodeToKeep.recordClass(pair.type);
}
if (hasCatchAll) {
dest.putUleb128(handler.catchAllAddr);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 0e0e1c9..4cc6fdf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.android.tools.r8.utils.PredicateUtils.not;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
@@ -758,7 +759,8 @@
if (candidateClass.type != hostType) {
DexProgramClass hostClass = asProgramClassOrNull(appView.definitionFor(hostType));
assert hostClass != null;
- if (!classMembersConflict(candidateClass, hostClass)) {
+ if (!classMembersConflict(candidateClass, hostClass)
+ && !hasMembersNotStaticized(candidateClass, staticizedMethods)) {
// Move all members of the candidate class into host class.
moveMembersIntoHost(staticizedMethods,
candidateClass, hostType, hostClass, methodMapping, fieldMapping);
@@ -779,6 +781,16 @@
|| Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
}
+ private boolean hasMembersNotStaticized(
+ DexProgramClass candidateClass, ProgramMethodSet staticizedMethods) {
+ // TODO(b/159174309): Refine the analysis to allow for fields.
+ if (candidateClass.hasFields()) {
+ return true;
+ }
+ // TODO(b/158018192): Activate again when picking up all references.
+ return candidateClass.methods(not(staticizedMethods::contains)).iterator().hasNext();
+ }
+
private void moveMembersIntoHost(
ProgramMethodSet staticizedMethods,
DexProgramClass candidateClass,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 3cb6218..23d3658 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
@@ -6,18 +6,26 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CheckCastInstructionSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
-import java.util.Iterator;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.collect.ImmutableSet;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,7 +36,8 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
- private static final String expectedOutput = StringUtils.lines("Hello, world");
+ private static final String expectedOutput =
+ StringUtils.lines("Caught java.time.format.DateTimeParseException", "true", "Hello, world");
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
@@ -47,15 +56,52 @@
}
private void checkRewrittenInvokes(CodeInspector inspector) {
+ Set<String> expectedInvokeHolders;
+ Set<String> expectedCatchGuards;
+ Set<String> expectedCheckCastType;
+ String expectedInstanceOfTypes;
if (parameters.getApiLevel().getLevel() >= 26) {
- return;
+ expectedInvokeHolders =
+ ImmutableSet.of("java.time.Clock", "java.time.LocalDate", "java.time.ZoneOffset");
+ expectedCatchGuards = ImmutableSet.of("java.time.format.DateTimeParseException");
+ expectedCheckCastType = ImmutableSet.of("java.time.ZoneId");
+ expectedInstanceOfTypes = "java.time.ZoneOffset";
+ } else {
+ expectedInvokeHolders =
+ ImmutableSet.of("j$.time.Clock", "j$.time.LocalDate", "j$.time.ZoneOffset");
+ expectedCatchGuards = ImmutableSet.of("j$.time.format.DateTimeParseException");
+ expectedCheckCastType = ImmutableSet.of("j$.time.ZoneId");
+ expectedInstanceOfTypes = "j$.time.ZoneOffset";
}
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
- Iterator<InvokeInstructionSubject> iterator =
- classSubject.uniqueMethodWithName("main").iterateInstructions(InstructionSubject::isInvoke);
- InvokeInstructionSubject invoke = iterator.next();
- assertTrue(invoke.holder().is("j$.time.Clock"));
+ MethodSubject main = classSubject.uniqueMethodWithName("main");
+ Set<String> foundInvokeHolders =
+ main.streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(
+ instructionSubject ->
+ ((InvokeInstructionSubject) instructionSubject).holder().toString())
+ .filter(holder -> holder.startsWith("j$.time.") || holder.startsWith("java.time."))
+ .collect(Collectors.toSet());
+ assertEquals(expectedInvokeHolders, foundInvokeHolders);
+ main.streamInstructions()
+ .filter(InstructionSubject::isCheckCast)
+ .map(InstructionSubject::asCheckCast)
+ .map(CheckCastInstructionSubject::getType)
+ .map(DexType::toSourceString)
+ .collect(Collectors.toSet())
+ .equals(expectedCheckCastType);
+ assertEquals(
+ 1,
+ main.streamInstructions().filter(io -> io.isInstanceOf(expectedInstanceOfTypes)).count());
+
+ Set<String> foundCatchGuards =
+ main.streamTryCatches()
+ .flatMap(TryCatchSubject::streamGuards)
+ .map(TypeSubject::toString)
+ .collect(Collectors.toSet());
+ assertEquals(expectedCatchGuards, foundCatchGuards);
}
@Test
@@ -84,6 +130,7 @@
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .enableInliningAnnotations()
.compile()
.inspect(this::checkRewrittenInvokes)
.addDesugaredCoreLibraryRunClassPath(
@@ -97,8 +144,28 @@
static class TestClass {
+ @NeverInline
+ public static Object newObjectInstance() {
+ return System.currentTimeMillis() > 0 ? new Object() : null;
+ }
+
+ @NeverInline
+ public static Object nullReference() {
+ return System.currentTimeMillis() > 0 ? null : new Object();
+ }
+
public static void main(String[] args) {
java.time.Clock.systemDefaultZone();
+ try {
+ java.time.LocalDate.parse("");
+ } catch (java.time.format.DateTimeParseException e) {
+ System.out.println("Caught java.time.format.DateTimeParseException");
+ }
+ java.time.ZoneId id = (java.time.ZoneId) nullReference();
+ if (newObjectInstance() instanceof java.time.ZoneOffset) {
+ System.out.println("NOT!");
+ }
+ System.out.println(java.time.ZoneOffset.getAvailableZoneIds().size() > 0);
System.out.println("Hello, world");
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java
new file mode 100644
index 0000000..0fcb3ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/HostWithStaticMethodTest.java
@@ -0,0 +1,81 @@
+// 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.ir.optimize.staticizer;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.staticizer.HostWithStaticMethodTest.Outer.SingletonHolder;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+// This is a reproduction of b/158018192.
+public class HostWithStaticMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public HostWithStaticMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Outer.class, SingletonHolder.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("foo", "bar", "foo");
+ }
+
+ @NeverClassInline
+ public static class Outer {
+
+ public static class SingletonHolder {
+
+ public static final Outer outer = new Outer();
+
+ @NeverInline
+ // This method should not be in conflict with any methods in Outer.
+ public static void foo2() {
+ foo();
+ }
+ }
+
+ @NeverInline
+ public static void foo() {
+ System.out.println("foo");
+ }
+
+ @NeverInline
+ public void bar() {
+ System.out.println("bar");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Outer.foo();
+ SingletonHolder.outer.bar();
+ SingletonHolder.foo2();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
index f28235b..5dabfaf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InstanceInsideCompanionTest.java
@@ -46,8 +46,9 @@
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutputLines("Candidate#foo(false)")
- .inspect(this::inspect);
+ .assertSuccessWithOutputLines("Candidate#foo(false)");
+ // TODO(b/159174309): Disable inspection until fixed.
+ // .inspect(this::inspect);
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
index e552581..4e3f691 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
@@ -59,11 +59,13 @@
assertThat(instance, not(isPresent()));
ClassSubject companion = inspector.clazz(Host.Companion.class);
- assertThat(companion, not(isPresent()));
+ // TODO(b/158018192): This should not be present.
+ assertThat(companion, isPresent());
// Check if the candidate methods are staticized (if necessary) and migrated.
for (String name : ImmutableList.of("boo", "foo")) {
- MethodSubject oo = host.uniqueMethodWithName(name);
+ // TODO(b/158018192): This should be host and not companion.
+ MethodSubject oo = companion.uniqueMethodWithName(name);
assertThat(oo, isPresent());
assertTrue(oo.isStatic());
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
index 3e47f3b..d6bac64 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
@@ -11,12 +11,17 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
class CfTryCatchSubject implements TryCatchSubject {
+ private final CodeInspector inspector;
private final CfCode cfCode;
private final CfTryCatch tryCatch;
- CfTryCatchSubject(CfCode cfCode, CfTryCatch tryCatch) {
+ CfTryCatchSubject(CodeInspector inspector, CfCode cfCode, CfTryCatch tryCatch) {
+ this.inspector = inspector;
this.cfCode = cfCode;
this.tryCatch = tryCatch;
}
@@ -62,6 +67,16 @@
}
@Override
+ public Stream<TypeSubject> streamGuards() {
+ return tryCatch.guards.stream().map(type -> new TypeSubject(inspector, type));
+ }
+
+ @Override
+ public Collection<TypeSubject> guards() {
+ return streamGuards().collect(Collectors.toList());
+ }
+
+ @Override
public int getNumberOfHandlers() {
return tryCatch.guards.size();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index b2edae2..3cfbb22 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -386,11 +386,11 @@
}
TryCatchSubject createTryCatchSubject(DexCode code, Try tryElement, TryHandler tryHandler) {
- return new DexTryCatchSubject(code, tryElement, tryHandler);
+ return new DexTryCatchSubject(this, code, tryElement, tryHandler);
}
TryCatchSubject createTryCatchSubject(CfCode code, CfTryCatch tryCatch) {
- return new CfTryCatchSubject(code, tryCatch);
+ return new CfTryCatchSubject(this, code, tryCatch);
}
TryCatchIterator createTryCatchIterator(MethodSubject method) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
index 4a96ff1..4624c33 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexTryCatchSubject.java
@@ -9,13 +9,20 @@
import com.android.tools.r8.graph.DexCode.Try;
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
class DexTryCatchSubject implements TryCatchSubject {
+ private final CodeInspector inspector;
private final DexCode dexCode;
private final Try tryElement;
private final TryHandler tryHandler;
- DexTryCatchSubject(DexCode dexCode, Try tryElement, TryHandler tryHandler) {
+ DexTryCatchSubject(
+ CodeInspector inspector, DexCode dexCode, Try tryElement, TryHandler tryHandler) {
+ this.inspector = inspector;
this.dexCode = dexCode;
this.tryElement = tryElement;
this.tryHandler = tryHandler;
@@ -45,6 +52,18 @@
}
@Override
+ public Stream<TypeSubject> streamGuards() {
+ return Arrays.stream(tryHandler.pairs)
+ .map(pair -> pair.type)
+ .map(type -> new TypeSubject(inspector, type));
+ }
+
+ @Override
+ public Collection<TypeSubject> guards() {
+ return streamGuards().collect(Collectors.toList());
+ }
+
+ @Override
public int getNumberOfHandlers() {
if (tryHandler.catchAllAddr != NO_HANDLER) {
return tryHandler.pairs.length + 1;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index b158404..72b71b0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -89,6 +89,10 @@
return Streams.stream(iterateInstructions());
}
+ public Stream<TryCatchSubject> streamTryCatches() {
+ return Streams.stream(iterateTryCatches());
+ }
+
public void getLineNumberForInstruction(InstructionSubject subject) {
assert hasLineNumberTable();
getLineNumberTable().getLineForInstruction(subject);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
index 73e9ef1..d34f4e9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TryCatchSubject.java
@@ -3,10 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
+import java.util.Collection;
+import java.util.stream.Stream;
+
public interface TryCatchSubject {
RangeSubject getRange();
boolean isCatching(String exceptionType);
boolean hasCatchAll();
+ Stream<TypeSubject> streamGuards();
+
+ Collection<TypeSubject> guards();
+
int getNumberOfHandlers();
}