Version 2.0.86

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

Bug: 158417777
Change-Id: I72052bed4e17abb4a01d25e8181bbcbca7b3a13b
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index bc4c231..80f3b35 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.0.85";
+  public static final String LABEL = "2.0.86";
 
   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 d549e28..0d1aa9c 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -123,10 +123,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 5cbd975..42f2065 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -507,6 +507,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/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/JavaTimeTest.java
index 732c0b9..bb51ec7 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,17 +6,25 @@
 
 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.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;
@@ -27,7 +35,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", "Hello, world");
 
   @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
   public static List<Object[]> data() {
@@ -41,15 +50,50 @@
   }
 
   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");
+      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");
+      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
@@ -78,6 +122,7 @@
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+        .enableInliningAnnotations()
         .compile()
         .inspect(this::checkRewrittenInvokes)
         .addDesugaredCoreLibraryRunClassPath(
@@ -91,8 +136,27 @@
 
   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("Hello, world");
     }
   }
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 663a070..edbf072 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;
   }
@@ -61,4 +66,13 @@
     return isCatching(DexItemFactory.throwableDescriptorString);
   }
 
+  @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());
+  }
 }
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 757216c..a628af9 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
@@ -391,11 +391,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 ed46590..e6ba14f 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;
@@ -44,4 +51,15 @@
     return tryHandler.catchAllAddr != NO_HANDLER;
   }
 
+  @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());
+  }
 }
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 af6e1a4..62f0682 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
@@ -86,6 +86,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 838f7a8..993ab7e 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,8 +3,15 @@
 // 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();
 }