Merge "Allow larger line ranges in map files for class file output"
diff --git a/build.gradle b/build.gradle
index ec53231..2671227 100644
--- a/build.gradle
+++ b/build.gradle
@@ -472,9 +472,11 @@
         // Default disable errorprone (enabled and setup below).
         options.errorprone.enabled = false
         options.compilerArgs << '-Xlint:unchecked'
+        // Run all compilation tasks in a forked subprocess.
+        options.fork = true
         // Javac often runs out of stack space when compiling the tests.
         // Increase the stack size for the javac process.
-        options.forkOptions.jvmArgs << "-Xss8m"
+        options.forkOptions.jvmArgs << "-Xss4m"
         // Set the bootclass path so compilation is consistent with 1.8 target compatibility.
         options.forkOptions.jvmArgs << "-Xbootclasspath/a:third_party/openjdk/openjdk-rt-1.8/rt.jar"
     }
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2d19d9d..4f472c6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -615,15 +615,17 @@
         new DiscardedChecker(rootSet, application, options).run();
       }
 
-      timing.begin("Minification");
-      // If we did not have keep rules, everything will be marked as keep, so no minification
-      // will happen. Just avoid the overhead.
-      NamingLens namingLens =
-          options.enableMinification
-              ? new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
-                  .run(timing)
-              : NamingLens.getIdentityLens();
-      timing.end();
+      // Perform minification.
+      NamingLens namingLens;
+      if (options.enableMinification) {
+        timing.begin("Minification");
+        namingLens =
+            new Minifier(appView.appInfo().withLiveness(), rootSet, desugaredCallSites, options)
+                .run(timing);
+        timing.end();
+      } else {
+        namingLens = NamingLens.getIdentityLens();
+      }
 
       ProguardMapSupplier proguardMapSupplier;
 
diff --git a/src/main/java/com/android/tools/r8/code/InstructionFactory.java b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
index 14d2f4b..83b0570 100644
--- a/src/main/java/com/android/tools/r8/code/InstructionFactory.java
+++ b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.code;
 
 import com.android.tools.r8.graph.OffsetToObjectMapping;
-import java.nio.ByteBuffer;
 import java.nio.ShortBuffer;
 import java.util.ArrayList;
 import java.util.List;
@@ -18,11 +17,6 @@
     return create(high, opcode, stream, mapping);
   }
 
-  public Instruction[] readSequenceFrom(ByteBuffer buffer, int startIndex, int length,
-      OffsetToObjectMapping mapping) {
-    return readSequenceFrom(buffer.asShortBuffer(), startIndex, length, mapping);
-  }
-
   public Instruction[] readSequenceFrom(ShortBuffer buffer, int startIndex, int length,
       OffsetToObjectMapping mapping) {
     ShortBufferBytecodeStream range =
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 825846e..26398d8 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -281,7 +281,7 @@
                     }
                     // Release use of the backing buffer now that accept has returned.
                     data.invalidate();
-                    byteBufferProvider.releaseByteBuffer(result.buffer);
+                    byteBufferProvider.releaseByteBuffer(result.buffer.asByteBuffer());
                     return true;
                   }));
         }
diff --git a/src/main/java/com/android/tools/r8/dex/BinaryReader.java b/src/main/java/com/android/tools/r8/dex/BinaryReader.java
index 5d51238..dd1cfee 100644
--- a/src/main/java/com/android/tools/r8/dex/BinaryReader.java
+++ b/src/main/java/com/android/tools/r8/dex/BinaryReader.java
@@ -10,14 +10,13 @@
 import com.android.tools.r8.utils.LebUtils;
 import com.android.tools.r8.utils.StreamUtils;
 import java.io.IOException;
-import java.nio.ByteBuffer;
 
 /**
  * Base class for reading binary content.
  */
 public abstract class BinaryReader {
   protected final Origin origin;
-  protected final ByteBuffer buffer;
+  protected final CompatByteBuffer buffer;
 
   protected BinaryReader(ProgramResource resource) throws ResourceException, IOException {
     this(resource.getOrigin(), StreamUtils.StreamToByteArrayClose(resource.getByteStream()));
@@ -26,7 +25,7 @@
   protected BinaryReader(Origin origin, byte[] bytes) {
     assert origin != null;
     this.origin = origin;
-    buffer = ByteBuffer.wrap(bytes);
+    buffer = CompatByteBuffer.wrap(bytes);
   }
 
   public Origin getOrigin() {
diff --git a/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java b/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java
new file mode 100644
index 0000000..6e32c6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java
@@ -0,0 +1,135 @@
+// 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.dex;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+
+/**
+ * In JDK 9 ByteBuffer ByteBuffer.position(int) started overriding Buffer Buffer.position(int) along
+ * with various other methods defined on java.nio.Buffer. To avoid issues with running R8 on JDK 8
+ * we internally wrap any use of ByteBuffer and make sure access to the overridden methods happens
+ * only at the super type Buffer which is known to be in both JDKs.
+ */
+public class CompatByteBuffer {
+
+  private final ByteBuffer buffer;
+
+  public CompatByteBuffer(ByteBuffer buffer) {
+    this.buffer = buffer;
+  }
+
+  public static CompatByteBuffer wrap(byte[] bytes) {
+    return new CompatByteBuffer(ByteBuffer.wrap(bytes));
+  }
+
+  private Buffer asBuffer() {
+    return buffer;
+  }
+
+  public ByteBuffer asByteBuffer() {
+    return buffer;
+  }
+
+  // ----------------------------------------------------------------------------------------------
+  // 1.8 compatible calls to java.nio.Buffer methods.
+  // ----------------------------------------------------------------------------------------------
+
+  // The position(int) became overridden with a ByteBuffer return value.
+  public void position(int newPosition) {
+    asBuffer().position(newPosition);
+  }
+
+  // The rewind() became overridden with a ByteBuffer return value.
+  public void rewind() {
+    asBuffer().rewind();
+  }
+
+  // ----------------------------------------------------------------------------------------------
+  // Methods on java.nio.Buffer that are safely overridden or not non-overridden.
+  // ----------------------------------------------------------------------------------------------
+
+  // Note: array() overrides Buffer.array() in JDK 8 too, so that is safe for JDK 8 and 9.
+  public byte[] array() {
+    return asByteBuffer().array();
+  }
+
+  public int arrayOffset() {
+    return asByteBuffer().arrayOffset();
+  }
+
+  public int capacity() {
+    return asByteBuffer().capacity();
+  }
+
+  public boolean hasArray() {
+    return asByteBuffer().hasArray();
+  }
+
+  public boolean hasRemaining() {
+    return asByteBuffer().hasRemaining();
+  }
+
+  public int position() {
+    return asByteBuffer().position();
+  }
+
+  public int remaining() {
+    return asByteBuffer().remaining();
+  }
+
+  // ----------------------------------------------------------------------------------------------
+  // Methods on java.nio.ByteBuffer.
+  // ----------------------------------------------------------------------------------------------
+
+  public ShortBuffer asShortBuffer() {
+    return asByteBuffer().asShortBuffer();
+  }
+
+  public void order(ByteOrder bo) {
+    asByteBuffer().order(bo);
+  }
+
+  public byte get() {
+    return asByteBuffer().get();
+  }
+
+  public byte get(int index) {
+    return asByteBuffer().get(index);
+  }
+
+  public void get(byte[] dst) {
+    asByteBuffer().get(dst);
+  }
+
+  public int getInt() {
+    return asByteBuffer().getInt();
+  }
+
+  public int getInt(int offset) {
+    return asByteBuffer().getInt(offset);
+  }
+
+  public short getShort() {
+    return asByteBuffer().getShort();
+  }
+
+  public void put(byte aByte) {
+    asByteBuffer().put(aByte);
+  }
+
+  public void putShort(short aShort) {
+    asByteBuffer().putShort(aShort);
+  }
+
+  public void putInt(int anInteger) {
+    asByteBuffer().putInt(anInteger);
+  }
+
+  public void put(byte[] bytes) {
+    asByteBuffer().put(bytes);
+  }
+}
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 000bc0e..2d1da51 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -22,7 +22,7 @@
   private static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
 
   private final ByteBufferProvider byteBufferProvider;
-  private ByteBuffer byteBuffer;
+  private CompatByteBuffer byteBuffer;
 
   @VisibleForTesting
   DexOutputBuffer() {
@@ -37,16 +37,16 @@
   private void ensureSpaceFor(int bytes) {
     if (byteBuffer.remaining() < bytes) {
       int newSize = byteBuffer.capacity() + Math.max(byteBuffer.capacity(), bytes * 2);
-      ByteBuffer newBuffer = allocateByteBuffer(newSize);
-      System.arraycopy(byteBuffer.array(), 0, newBuffer.array(), 0, byteBuffer.position());
+      CompatByteBuffer newBuffer = allocateByteBuffer(newSize);
+      System.arraycopy(byteBuffer.array(), 0, newBuffer.array(), 0, position());
       newBuffer.position(byteBuffer.position());
       freeByteBuffer(byteBuffer);
       byteBuffer = newBuffer;
     }
   }
 
-  private ByteBuffer allocateByteBuffer(int size) {
-    ByteBuffer buffer = byteBufferProvider.acquireByteBuffer(size);
+  private CompatByteBuffer allocateByteBuffer(int size) {
+    CompatByteBuffer buffer = new CompatByteBuffer(byteBufferProvider.acquireByteBuffer(size));
     if (!buffer.hasArray()) {
       throw new CompilationError(
           "Provided byte-buffer is required to have an array backing, but does not.");
@@ -69,9 +69,9 @@
     return buffer;
   }
 
-  private void freeByteBuffer(ByteBuffer buffer) {
+  private void freeByteBuffer(CompatByteBuffer buffer) {
     assert buffer != null;
-    byteBufferProvider.releaseByteBuffer(buffer);
+    byteBufferProvider.releaseByteBuffer(buffer.asByteBuffer());
   }
 
   public void putUleb128(int value) {
@@ -133,7 +133,7 @@
   public int align(int bytes) {
     assert bytes > 0;
     int mask = bytes - 1;
-    int newPosition = (byteBuffer.position() + mask) & ~mask;
+    int newPosition = (position() + mask) & ~mask;
     ensureSpaceFor(newPosition - position());
     byteBuffer.position(newPosition);
     return newPosition;
@@ -165,8 +165,8 @@
     return byteBuffer.array();
   }
 
-  public ByteBuffer stealByteBuffer() {
-    ByteBuffer buffer = byteBuffer;
+  public CompatByteBuffer stealByteBuffer() {
+    CompatByteBuffer buffer = byteBuffer;
     byteBuffer = null;
     return buffer;
   }
diff --git a/src/main/java/com/android/tools/r8/dex/DexReader.java b/src/main/java/com/android/tools/r8/dex/DexReader.java
index d8997f7..45a0696 100644
--- a/src/main/java/com/android/tools/r8/dex/DexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexReader.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.utils.DexVersion;
 import java.io.IOException;
 import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
 /**
@@ -38,7 +37,7 @@
   }
 
   // Parse the magic header and determine the dex file version.
-  private int parseMagic(ByteBuffer buffer) {
+  private int parseMagic(CompatByteBuffer buffer) {
     try {
       buffer.get();
       buffer.rewind();
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 1af80fa..4197da5 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -57,7 +57,6 @@
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import java.nio.ByteBuffer;
 import java.security.MessageDigest;
 import java.util.Arrays;
 import java.util.Collection;
@@ -77,10 +76,10 @@
   /** Simple pair of a byte buffer and its written length. */
   public static class ByteBufferResult {
     // Ownership of the buffer is transferred to the receiver of this result structure.
-    public final ByteBuffer buffer;
+    public final CompatByteBuffer buffer;
     public final int length;
 
-    private ByteBufferResult(ByteBuffer buffer, int length) {
+    private ByteBufferResult(CompatByteBuffer buffer, int length) {
       this.buffer = buffer;
       this.length = length;
     }
diff --git a/src/main/java/com/android/tools/r8/dex/VDexReader.java b/src/main/java/com/android/tools/r8/dex/VDexReader.java
index a3a5a2f..8d6f2d0 100644
--- a/src/main/java/com/android/tools/r8/dex/VDexReader.java
+++ b/src/main/java/com/android/tools/r8/dex/VDexReader.java
@@ -14,7 +14,6 @@
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
 /**
@@ -37,7 +36,7 @@
   }
 
   // Parse the magic header and determine the dex file version.
-  private int parseMagic(ByteBuffer buffer) {
+  private int parseMagic(CompatByteBuffer buffer) {
     int index = 0;
     for (byte prefixByte : VDEX_FILE_MAGIC_PREFIX) {
       if (buffer.get(index++) != prefixByte) {
diff --git a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
index e3fa01b..88b3b4b 100644
--- a/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
+++ b/src/main/java/com/android/tools/r8/experimental/graphinfo/GraphEdgeInfo.java
@@ -15,6 +15,7 @@
     TargetedBySuper,
     InvokedFrom,
     InvokedFromLambdaCreatedIn,
+    AnnotatedOn,
     ReferencedFrom,
     ReflectiveUseFrom,
     ReachableFromLiveType,
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 28aabea..5e65aa3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -38,22 +38,17 @@
   public DexTypeList interfaces;
   public DexString sourceFile;
 
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedField[] staticFields;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedField[] instanceFields;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedMethod[] directMethods;
-  /**
-   * Access has to be synchronized during concurrent collection/writing phase.
-   */
-  protected DexEncodedMethod[] virtualMethods;
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedField[] staticFields = NO_FIELDS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedField[] instanceFields = NO_FIELDS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedMethod[] directMethods = NO_METHODS;
+
+  /** Access has to be synchronized during concurrent collection/writing phase. */
+  protected DexEncodedMethod[] virtualMethods = NO_METHODS;
 
   /** Enclosing context of this class if it is an inner class, null otherwise. */
   private EnclosingMethodAttribute enclosingMethod;
@@ -146,7 +141,8 @@
     System.arraycopy(directMethods, 0, newMethods, 0, directMethods.length);
     newMethods[directMethods.length] = method;
     directMethods = newMethods;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void appendDirectMethods(Collection<DexEncodedMethod> methods) {
@@ -158,7 +154,8 @@
       i++;
     }
     directMethods = newMethods;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolders(methods);
+    assert verifyNoDuplicateMethods();
   }
 
   public void removeDirectMethod(int index) {
@@ -170,12 +167,14 @@
 
   public void setDirectMethod(int index, DexEncodedMethod method) {
     directMethods[index] = method;
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void setDirectMethods(DexEncodedMethod[] values) {
     directMethods = MoreObjects.firstNonNull(values, NO_METHODS);
-    assert verifyNoDuplicateMethods(directMethods);
+    assert verifyCorrectnessOfMethodHolders(directMethods());
+    assert verifyNoDuplicateMethods();
   }
 
   public List<DexEncodedMethod> virtualMethods() {
@@ -191,7 +190,8 @@
     System.arraycopy(virtualMethods, 0, newMethods, 0, virtualMethods.length);
     newMethods[virtualMethods.length] = method;
     virtualMethods = newMethods;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void appendVirtualMethods(Collection<DexEncodedMethod> methods) {
@@ -203,7 +203,8 @@
       i++;
     }
     virtualMethods = newMethods;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolders(methods);
+    assert verifyNoDuplicateMethods();
   }
 
   public void removeVirtualMethod(int index) {
@@ -216,22 +217,39 @@
 
   public void setVirtualMethod(int index, DexEncodedMethod method) {
     virtualMethods[index] = method;
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolder(method);
+    assert verifyNoDuplicateMethods();
   }
 
   public void setVirtualMethods(DexEncodedMethod[] values) {
     virtualMethods = MoreObjects.firstNonNull(values, NO_METHODS);
-    assert verifyNoDuplicateMethods(virtualMethods);
+    assert verifyCorrectnessOfMethodHolders(virtualMethods());
+    assert verifyNoDuplicateMethods();
   }
 
-  private boolean verifyNoDuplicateMethods(DexEncodedMethod[] methods) {
+  private boolean verifyCorrectnessOfMethodHolder(DexEncodedMethod method) {
+    assert method.method.holder == type
+        : "Expected method `"
+            + method.method.toSourceString()
+            + "` to have holder `"
+            + type.toSourceString()
+            + "`";
+    return true;
+  }
+
+  private boolean verifyCorrectnessOfMethodHolders(Iterable<DexEncodedMethod> methods) {
+    for (DexEncodedMethod method : methods) {
+      assert verifyCorrectnessOfMethodHolder(method);
+    }
+    return true;
+  }
+
+  private boolean verifyNoDuplicateMethods() {
     Set<DexMethod> unique = Sets.newIdentityHashSet();
-    Arrays.stream(methods)
-        .forEach(
-            method -> {
-              boolean changed = unique.add(method.method);
-              assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
-            });
+    for (DexEncodedMethod method : methods()) {
+      boolean changed = unique.add(method.method);
+      assert changed : "Duplicate method `" + method.method.toSourceString() + "`";
+    }
     return true;
   }
 
@@ -651,6 +669,8 @@
   public boolean isValid() {
     assert !isInterface()
         || Arrays.stream(virtualMethods).noneMatch(method -> method.accessFlags.isFinal());
+    assert verifyCorrectnessOfMethodHolders(methods());
+    assert verifyNoDuplicateMethods();
     return true;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 1757459..e7b3967 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -140,6 +140,8 @@
   public final DexString contentEqualsMethodName = createString("contentEquals");
   public final DexString indexOfMethodName = createString("indexOf");
   public final DexString lastIndexOfMethodName = createString("lastIndexOf");
+  public final DexString compareToMethodName = createString("compareTo");
+  public final DexString compareToIgnoreCaseMethodName = createString("compareToIgnoreCase");
   public final DexString cloneMethodName = createString("clone");
 
   public final DexString valueOfMethodName = createString("valueOf");
@@ -613,6 +615,8 @@
     public final DexMethod indexOfString;
     public final DexMethod lastIndexOfInt;
     public final DexMethod lastIndexOfString;
+    public final DexMethod compareTo;
+    public final DexMethod compareToIgnoreCase;
 
     public final DexMethod valueOf;
     public final DexMethod toString;
@@ -649,6 +653,11 @@
           createMethod(stringDescriptor, lastIndexOfMethodName, intDescriptor, needsOneString);
       lastIndexOfInt =
           createMethod(stringDescriptor, lastIndexOfMethodName, intDescriptor, needsOneInt);
+      compareTo =
+          createMethod(stringDescriptor, compareToMethodName, intDescriptor, needsOneString);
+      compareToIgnoreCase =
+          createMethod(stringDescriptor, compareToIgnoreCaseMethodName, intDescriptor,
+              needsOneString);
 
       valueOf = createMethod(
           stringDescriptor, valueOfMethodName, stringDescriptor, needsOneObject);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 55bc508..977c90b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -292,6 +292,13 @@
     debugData = null;
   }
 
+  public boolean hasSameOrNoLocal(Value other) {
+    assert other != null;
+    return hasLocalInfo()
+        ? other.getLocalInfo() == this.getLocalInfo()
+        : !other.hasLocalInfo();
+  }
+
   public List<Instruction> getDebugLocalStarts() {
     if (debugData == null) {
       return Collections.emptyList();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 30712e6..f7baf3e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -48,6 +48,7 @@
 import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsage;
 import com.android.tools.r8.graph.ParameterUsagesInfo.ParameterUsageBuilder;
 import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
+import com.android.tools.r8.ir.analysis.type.Nullability;
 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.AlwaysMaterializingNop;
@@ -1632,10 +1633,30 @@
       Instruction current = iterator.next();
       if (current.isInvokeMethod()) {
         InvokeMethod invoke = current.asInvokeMethod();
-        if (invoke.outValue() != null && !invoke.outValue().hasLocalInfo()) {
+        Value outValue = invoke.outValue();
+        // TODO(b/124246610): extend to other variants that receive error messages or supplier.
+        if (invoke.getInvokedMethod() == dexItemFactory.objectsMethods.requireNonNull) {
+          Value obj = invoke.arguments().get(0);
+          if ((outValue == null && obj.hasLocalInfo())
+              || (outValue != null && !obj.hasSameOrNoLocal(outValue))) {
+            continue;
+          }
+          Nullability nullability = obj.getTypeLattice().nullability();
+          if (nullability.isDefinitelyNotNull()) {
+            if (outValue != null) {
+              outValue.replaceUsers(obj);
+              needToNarrowValues.addAll(outValue.affectedValues());
+            }
+            iterator.removeOrReplaceByDebugLocalRead();
+          } else if (nullability.isDefinitelyNull()) {
+            // TODO(b/124246610): throw NPE.
+            // Refactor UninstantiatedTypeOptimization#replaceCurrentInstructionWithThrowNull
+            // and move it to iterator.
+          }
+        } else if (outValue != null && !outValue.hasLocalInfo()) {
           if (libraryMethodsReturningReceiver.contains(invoke.getInvokedMethod())) {
             if (checkArgumentType(invoke, 0)) {
-              invoke.outValue().replaceUsers(invoke.arguments().get(0));
+              outValue.replaceUsers(invoke.arguments().get(0));
               invoke.setOutValue(null);
             }
           } else if (appInfoWithLiveness != null) {
@@ -1650,7 +1671,6 @@
                 // Replace the out value of the invoke with the argument and ignore the out value.
                 if (argumentIndex >= 0 && checkArgumentType(invoke, argumentIndex)) {
                   Value argument = invoke.arguments().get(argumentIndex);
-                  Value outValue = invoke.outValue();
                   assert outValue.verifyCompatible(argument.outType());
                   if (argument.getTypeLattice().lessThanOrEqual(
                       outValue.getTypeLattice(), appInfo)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 3fc442a..5dfbb78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.EscapeAnalysis;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -53,16 +54,18 @@
 
   // int String#length()
   // boolean String#isEmpty()
-  // boolean String#startsWith(str)
-  // boolean String#endsWith(str)
-  // boolean String#contains(str)
-  // boolean String#equals(str)
-  // boolean String#equalsIgnoreCase(str)
-  // boolean String#contentEquals(str)
-  // int String#indexOf(str)
+  // boolean String#startsWith(String)
+  // boolean String#endsWith(String)
+  // boolean String#contains(String)
+  // boolean String#equals(String)
+  // boolean String#equalsIgnoreCase(String)
+  // boolean String#contentEquals(String)
+  // int String#indexOf(String)
   // int String#indexOf(int)
-  // int String#lastIndexOf(str)
+  // int String#lastIndexOf(String)
   // int String#lastIndexOf(int)
+  // int String#compareTo(String)
+  // int String#compareToIgnoreCase(String)
   public void computeTrivialOperationsOnConstString(IRCode code) {
     if (!code.hasConstString) {
       return;
@@ -102,6 +105,10 @@
         operatorWithInt = String::lastIndexOf;
       } else if (invokedMethod == factory.stringMethods.lastIndexOfString) {
         operatorWithString = String::lastIndexOf;
+      } else if (invokedMethod == factory.stringMethods.compareTo) {
+        operatorWithString = String::compareTo;
+      } else if (invokedMethod == factory.stringMethods.compareToIgnoreCase) {
+        operatorWithString = String::compareToIgnoreCase;
       } else {
         continue;
       }
@@ -111,12 +118,12 @@
           || !rcv.isConstant()) {
         continue;
       }
+      DexString rcvString = rcv.definition.asConstString().getValue();
 
       ConstNumber constNumber;
       if (operatorWithNoArg != null) {
         assert invoke.inValues().size() == 1;
-        ConstString rcvString = rcv.definition.asConstString();
-        int v = operatorWithNoArg.apply(rcvString.getValue().toString());
+        int v = operatorWithNoArg.apply(rcvString.toString());
         constNumber = code.createIntConstant(v);
       } else if (operatorWithString != null) {
         assert invoke.inValues().size() == 2;
@@ -126,10 +133,9 @@
             || !arg.isConstant()) {
           continue;
         }
-        ConstString rcvString = rcv.definition.asConstString();
-        ConstString argString = arg.definition.asConstString();
         int v = operatorWithString.apply(
-            rcvString.getValue().toString(), argString.getValue().toString());
+            rcvString.toString(),
+            arg.definition.asConstString().getValue().toString());
         constNumber = code.createIntConstant(v);
       } else {
         assert operatorWithInt != null;
@@ -140,10 +146,9 @@
             || !arg.isConstant()) {
           continue;
         }
-        ConstString rcvString = rcv.definition.asConstString();
-        ConstNumber argInt = arg.definition.asConstNumber();
         int v = operatorWithInt.apply(
-            rcvString.getValue().toString(), argInt.getIntValue());
+            rcvString.toString(),
+            arg.definition.asConstNumber().getIntValue());
         constNumber = code.createIntConstant(v);
       }
 
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index bbb3391..90dbbdb 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.Map;
 import java.util.function.Function;
@@ -33,7 +34,7 @@
     }
   }
 
-  Map<DexField, DexString> computeRenaming(Timing timing) {
+  FieldRenaming computeRenaming(Timing timing) {
     // Reserve names in all classes first. We do this in subtyping order so we do not
     // shadow a reserved field in subclasses. While there is no concept of virtual field
     // dispatch in Java, field resolution still traverses the super type chain and external
@@ -55,7 +56,20 @@
     timing.begin("rename-references");
     renameNonReboundReferences();
     timing.end();
-    return renaming;
+    return new FieldRenaming(renaming);
+  }
+
+  static class FieldRenaming {
+
+    final Map<DexField, DexString> renaming;
+
+    private FieldRenaming(Map<DexField, DexString> renaming) {
+      this.renaming = renaming;
+    }
+
+    public static FieldRenaming empty() {
+      return new FieldRenaming(ImmutableMap.of());
+    }
   }
 
   private void reserveNamesInSubtypes(DexType type, NamingState<DexType, ?> state) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 5f990ac..21e6bf9 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -96,6 +97,8 @@
   private final Equivalence<DexMethod> equivalence;
   private final Map<DexCallSite, DexString> callSiteRenaming = new IdentityHashMap<>();
 
+  private final Map<DexType, DexType> frontierMap = new IdentityHashMap<>();
+
   MethodNameMinifier(
       AppInfoWithLiveness appInfo,
       RootSet rootSet,
@@ -121,6 +124,7 @@
   }
 
   static class MethodRenaming {
+
     final Map<DexMethod, DexString> renaming;
     final Map<DexCallSite, DexString> callSiteRenaming;
 
@@ -129,26 +133,27 @@
       this.renaming = renaming;
       this.callSiteRenaming = callSiteRenaming;
     }
+
+    public static MethodRenaming empty() {
+      return new MethodRenaming(ImmutableMap.of(), ImmutableMap.of());
+    }
   }
 
   MethodRenaming computeRenaming(Timing timing) {
     // Phase 1: Reserve all the names that need to be kept and allocate linked state in the
     //          library part.
     timing.begin("Phase 1");
-    Map<DexType, DexType> frontierMap = new IdentityHashMap<>();
-    reserveNamesInClasses(appInfo.dexItemFactory.objectType,
-        appInfo.dexItemFactory.objectType,
-        null, frontierMap);
+    reserveNamesInClasses(
+        appInfo.dexItemFactory.objectType, appInfo.dexItemFactory.objectType, null);
     timing.end();
     // Phase 2: Reserve all the names that are required for interfaces.
     timing.begin("Phase 2");
-    DexType.forAllInterfaces(
-        appInfo.dexItemFactory, iface -> reserveNamesInInterfaces(iface, frontierMap));
+    DexType.forAllInterfaces(appInfo.dexItemFactory, this::reserveNamesInInterfaces);
     timing.end();
     // Phase 3: Assign names to interface methods. These are assigned by finding a name that is
     //          free in all naming states that may hold an implementation.
     timing.begin("Phase 3");
-    assignNamesToInterfaceMethods(frontierMap, timing);
+    assignNamesToInterfaceMethods(timing);
     timing.end();
     // Phase 4: Assign names top-down by traversing the subtype hierarchy.
     timing.begin("Phase 4");
@@ -201,8 +206,7 @@
     }
   }
 
-  private Set<NamingState<DexProto, ?>> getReachableStates(DexType type,
-      Map<DexType, DexType> frontierMap) {
+  private Set<NamingState<DexProto, ?>> getReachableStates(DexType type) {
     Set<DexType> interfaces = Sets.newIdentityHashSet();
     interfaces.add(type);
     collectSuperInterfaces(type, interfaces);
@@ -221,7 +225,7 @@
     return reachableStates;
   }
 
-  private void assignNamesToInterfaceMethods(Map<DexType, DexType> frontierMap, Timing timing) {
+  private void assignNamesToInterfaceMethods(Timing timing) {
     // First compute a map from method signatures to a set of naming states for interfaces and
     // frontier states of classes that implement them. We add the frontier states so that we can
     // reserve the names for later method naming.
@@ -232,17 +236,20 @@
     Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap = new HashMap<>();
     // A map from DexMethods to the first interface state it was seen in. Used to pick good names.
     Map<Wrapper<DexMethod>, NamingState<DexProto, ?>> originStates = new HashMap<>();
-    DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
-      assert iface.isInterface();
-      DexClass clazz = appInfo.definitionFor(iface);
-      if (clazz != null) {
-        Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(iface, frontierMap);
-        for (DexEncodedMethod method : shuffleMethods(clazz.methods())) {
-          addStatesToGlobalMapForMethod(
-              method, collectedStates, globalStateMap, sourceMethodsMap, originStates, iface);
-        }
-      }
-    });
+    DexType.forAllInterfaces(
+        appInfo.dexItemFactory,
+        iface -> {
+          assert iface.isInterface();
+          DexClass clazz = appInfo.definitionFor(iface);
+          if (clazz != null) {
+            assert clazz.isInterface();
+            Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(iface);
+            for (DexEncodedMethod method : shuffleMethods(clazz.methods())) {
+              addStatesToGlobalMapForMethod(
+                  method, collectedStates, globalStateMap, sourceMethodsMap, originStates, iface);
+            }
+          }
+        });
     // Collect the live call sites for multi-interface lambda expression renaming. For code with
     // desugared lambdas this is a conservative estimate, as we don't track if the generated
     // lambda classes survive into the output. As multi-interface lambda expressions are rare
@@ -272,7 +279,7 @@
           for (DexEncodedMethod method : implementedMethods) {
             DexType iface = method.method.holder;
             assert iface.isInterface();
-            Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(iface, frontierMap);
+            Set<NamingState<DexProto, ?>> collectedStates = getReachableStates(iface);
             addStatesToGlobalMapForMethod(
                 method, collectedStates, globalStateMap, sourceMethodsMap, originStates, iface);
             callSiteMethods.add(equivalence.wrap(method.method));
@@ -434,34 +441,33 @@
     return false;
   }
 
-  private void reserveNamesInClasses(DexType type, DexType libraryFrontier,
-      NamingState<DexProto, ?> parent,
-      Map<DexType, DexType> frontierMap) {
+  private void reserveNamesInClasses(
+      DexType type, DexType libraryFrontier, NamingState<DexProto, ?> parent) {
     assert !type.isInterface();
     DexClass holder = appInfo.definitionFor(type);
-    NamingState<DexProto, ?> state = allocateNamingStateAndReserve(holder, type, libraryFrontier,
-        parent, frontierMap);
+    NamingState<DexProto, ?> state =
+        allocateNamingStateAndReserve(holder, type, libraryFrontier, parent);
     // If this is a library class (or effectively a library class as it is missing) move the
     // frontier forward.
-    type.forAllExtendsSubtypes(subtype -> {
-      assert !subtype.isInterface();
-      reserveNamesInClasses(subtype,
-          holder == null || holder.isLibraryClass() ? subtype : libraryFrontier,
-          state, frontierMap);
-    });
+    type.forAllExtendsSubtypes(
+        subtype -> {
+          assert !subtype.isInterface();
+          reserveNamesInClasses(
+              subtype,
+              holder == null || holder.isLibraryClass() ? subtype : libraryFrontier,
+              state);
+        });
   }
 
-  private void reserveNamesInInterfaces(DexType type, Map<DexType, DexType> frontierMap) {
+  private void reserveNamesInInterfaces(DexType type) {
     assert type.isInterface();
     frontierMap.put(type, type);
     DexClass holder = appInfo.definitionFor(type);
-    allocateNamingStateAndReserve(holder, type, type, null, frontierMap);
+    allocateNamingStateAndReserve(holder, type, type, null);
   }
 
-  private NamingState<DexProto, ?> allocateNamingStateAndReserve(DexClass holder, DexType type,
-      DexType libraryFrontier,
-      NamingState<DexProto, ?> parent,
-      Map<DexType, DexType> frontierMap) {
+  private NamingState<DexProto, ?> allocateNamingStateAndReserve(
+      DexClass holder, DexType type, DexType libraryFrontier, NamingState<DexProto, ?> parent) {
     frontierMap.put(type, libraryFrontier);
     NamingState<DexProto, ?> state =
         computeStateIfAbsent(
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index e4ccbcd..becc643 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
 import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -56,21 +57,28 @@
     ClassNameMinifier classNameMinifier = new ClassNameMinifier(appInfo, rootSet, options);
     ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
+
+    assert new MinifiedRenaming(
+            classRenaming, MethodRenaming.empty(), FieldRenaming.empty(), appInfo)
+        .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
         new MethodNameMinifier(appInfo, rootSet, desugaredCallSites, options)
             .computeRenaming(timing);
     timing.end();
+
+    assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
+        .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+
     timing.begin("MinifyFields");
-    Map<DexField, DexString> fieldRenaming =
+    FieldRenaming fieldRenaming =
         new FieldNameMinifier(appInfo, rootSet, options).computeRenaming(timing);
     timing.end();
-    NamingLens lens =
-        new MinifiedRenaming(
-            classRenaming,
-            methodRenaming,
-            fieldRenaming,
-            appInfo);
+
+    NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
+    assert lens.verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
+
     timing.begin("MinifyIdentifiers");
     new IdentifierMinifier(
         appInfo, options.getProguardConfiguration().getAdaptClassStrings(), lens).run();
@@ -87,14 +95,14 @@
     private MinifiedRenaming(
         ClassRenaming classRenaming,
         MethodRenaming methodRenaming,
-        Map<DexField, DexString> fieldRenaming,
+        FieldRenaming fieldRenaming,
         AppInfo appInfo) {
       this.appInfo = appInfo;
       this.packageRenaming = classRenaming.packageRenaming;
       renaming.putAll(classRenaming.classRenaming);
       renaming.putAll(methodRenaming.renaming);
       renaming.putAll(methodRenaming.callSiteRenaming);
-      renaming.putAll(fieldRenaming);
+      renaming.putAll(fieldRenaming.renaming);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index cee867e..aecae1e 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -7,10 +7,14 @@
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 
 import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -19,7 +23,10 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -70,6 +77,40 @@
     return lookupName(reference.asDexField());
   }
 
+  public final DexField lookupField(DexField field, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createField(
+        lookupType(field.clazz, dexItemFactory),
+        lookupType(field.type, dexItemFactory),
+        lookupName(field));
+  }
+
+  public final DexMethod lookupMethod(DexMethod method, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createMethod(
+        lookupType(method.holder, dexItemFactory),
+        lookupProto(method.proto, dexItemFactory),
+        lookupName(method));
+  }
+
+  private DexProto lookupProto(DexProto proto, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createProto(
+        lookupType(proto.returnType, dexItemFactory),
+        Arrays.stream(proto.parameters.values)
+            .map(type -> lookupType(type, dexItemFactory))
+            .toArray(DexType[]::new));
+  }
+
+  public final DexType lookupType(DexType type, DexItemFactory dexItemFactory) {
+    if (type.isPrimitiveType() || type.isVoidType()) {
+      return type;
+    }
+    if (type.isArrayType()) {
+      DexType newBaseType = lookupType(type.toBaseType(dexItemFactory), dexItemFactory);
+      return type.replaceBaseType(newBaseType, dexItemFactory);
+    }
+    assert type.isClassType();
+    return dexItemFactory.createType(lookupDescriptor(type));
+  }
+
   public static NamingLens getIdentityLens() {
     return new IdentityLens();
   }
@@ -97,6 +138,34 @@
    */
   public abstract boolean checkTargetCanBeTranslated(DexMethod item);
 
+  public final boolean verifyNoCollisions(
+      Iterable<DexProgramClass> classes, DexItemFactory dexItemFactory) {
+    Set<DexReference> references = Sets.newIdentityHashSet();
+    for (DexProgramClass clazz : classes) {
+      {
+        DexType newType = lookupType(clazz.type, dexItemFactory);
+        boolean referencesChanged = references.add(newType);
+        assert referencesChanged
+            : "Duplicate definition of type `" + newType.toSourceString() + "`";
+      }
+
+      for (DexEncodedField field : clazz.fields()) {
+        DexField newField = lookupField(field.field, dexItemFactory);
+        boolean referencesChanged = references.add(newField);
+        assert referencesChanged
+            : "Duplicate definition of field `" + newField.toSourceString() + "`";
+      }
+
+      for (DexEncodedMethod method : clazz.methods()) {
+        DexMethod newMethod = lookupMethod(method.method, dexItemFactory);
+        boolean referencesChanged = references.add(newMethod);
+        assert referencesChanged
+            : "Duplicate definition of method `" + newMethod.toSourceString() + "`";
+      }
+    }
+    return true;
+  }
+
   private static class IdentityLens extends NamingLens {
 
     private IdentityLens() {
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 a51ca83..f011931 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -177,10 +177,10 @@
    * for these.
    */
   private final Set<DexType> liveTypes = Sets.newIdentityHashSet();
-  /**
-   * Set of annotation types that are instantiated.
-   */
-  private final Set<DexType> instantiatedAnnotations = Sets.newIdentityHashSet();
+
+  /** Set of annotation types that are instantiated. */
+  private final SetWithReason<DexAnnotation> instantiatedAnnotations =
+      new SetWithReason<>(this::registerAnnotation);
   /** Set of types that are actually instantiated. These cannot be abstract. */
   private final SetWithReason<DexType> instantiatedTypes = new SetWithReason<>(this::registerType);
   /**
@@ -803,9 +803,6 @@
           }
         }
       }
-      if (!holder.annotations.isEmpty()) {
-        processAnnotations(holder.annotations.annotations);
-      }
       // We also need to add the corresponding <clinit> to the set of live methods, as otherwise
       // static field initialization (and other class-load-time sideeffects) will not happen.
       KeepReason reason = KeepReason.reachableFromLiveType(type);
@@ -821,10 +818,15 @@
         enqueueFirstNonSerializableClassInitializer(holder, reason);
       }
 
-      // If this type has deferred annotations, we have to process those now, too.
-      Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
-      if (annotations != null) {
-        annotations.forEach(this::handleAnnotationOfLiveType);
+      if (!holder.isLibraryClass()) {
+        if (!holder.annotations.isEmpty()) {
+          processAnnotations(holder, holder.annotations.annotations);
+        }
+        // If this type has deferred annotations, we have to process those now, too.
+        Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
+        if (annotations != null) {
+          annotations.forEach(annotation -> handleAnnotationOfLiveType(holder, annotation));
+        }
       }
 
       Map<DexReference, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
@@ -834,36 +836,33 @@
     }
   }
 
-  private void handleAnnotationOfLiveType(DexAnnotation annotation) {
-    DexType type = annotation.annotation.type;
-    // Record that it is instantiated if it should be kept when its type is live.
-    if (shouldKeepAnnotation(annotation, true, appInfo.dexItemFactory, options)) {
-      instantiatedAnnotations.add(type);
-    }
-    AnnotationReferenceMarker referenceMarker = new AnnotationReferenceMarker(
-        annotation.annotation.type, appInfo.dexItemFactory);
-    annotation.annotation.collectIndexedItems(referenceMarker);
+  private void handleAnnotationOfLiveType(DexDefinition holder, DexAnnotation annotation) {
+    handleAnnotation(holder, annotation, true);
   }
 
-  private void processAnnotations(DexAnnotation[] annotations) {
+  private void processAnnotations(DexDefinition holder, DexAnnotation[] annotations) {
     for (DexAnnotation annotation : annotations) {
-      processAnnotation(annotation);
+      processAnnotation(holder, annotation);
     }
   }
 
-  private void processAnnotation(DexAnnotation annotation) {
+  private void processAnnotation(DexDefinition holder, DexAnnotation annotation) {
+    handleAnnotation(holder, annotation, false);
+  }
+
+  private void handleAnnotation(DexDefinition holder, DexAnnotation annotation, boolean forceLive) {
+    assert !holder.isDexClass() || !holder.asDexClass().isLibraryClass();
     DexType type = annotation.annotation.type;
-    if (liveTypes.contains(type)) {
-      // The type of this annotation is already live, so pick up its dependencies.
-      handleAnnotationOfLiveType(annotation);
-    } else {
-      // Record that it is instantiated if it should be kept although its type is not live.
-      if (shouldKeepAnnotation(annotation, false, appInfo.dexItemFactory, options)) {
-        instantiatedAnnotations.add(type);
-      }
+    boolean isLive = forceLive || liveTypes.contains(type);
+    if (!shouldKeepAnnotation(annotation, isLive, appInfo.dexItemFactory, options)) {
       // Remember this annotation for later.
       deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
+      return;
     }
+    instantiatedAnnotations.add(annotation, KeepReason.annotatedOn(holder));
+    AnnotationReferenceMarker referenceMarker =
+        new AnnotationReferenceMarker(annotation.annotation.type, appInfo.dexItemFactory);
+    annotation.annotation.collectIndexedItems(referenceMarker);
   }
 
   private void handleInvokeOfStaticTarget(DexMethod method, KeepReason reason) {
@@ -956,8 +955,11 @@
     }
     markTypeAsLive(method.method.holder);
     markParameterAndReturnTypesAsLive(method);
-    processAnnotations(method.annotations.annotations);
-    method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
+    if (!appInfo.definitionFor(method.method.holder).isLibraryClass()) {
+      processAnnotations(method, method.annotations.annotations);
+      method.parameterAnnotationsList.forEachAnnotation(
+          annotation -> processAnnotation(method, annotation));
+    }
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Method `%s` is targeted.", method.method);
     }
@@ -1614,8 +1616,11 @@
         }
       }
       markParameterAndReturnTypesAsLive(method);
-      processAnnotations(method.annotations.annotations);
-      method.parameterAnnotationsList.forEachAnnotation(this::processAnnotation);
+      if (!appInfo.definitionFor(method.method.holder).isLibraryClass()) {
+        processAnnotations(method, method.annotations.annotations);
+        method.parameterAnnotationsList.forEachAnnotation(
+            annotation -> processAnnotation(method, annotation));
+      }
       method.registerCodeReferences(new UseRegistry(options.itemFactory, method));
       // Add all dependent members to the workqueue.
       enqueueRootItems(rootSet.getDependentItems(method));
@@ -2013,8 +2018,11 @@
       super(appInfo);
       this.liveTypes = ImmutableSortedSet.copyOf(
           PresortedComparable<DexType>::slowCompareTo, enqueuer.liveTypes);
-      this.instantiatedAnnotations = ImmutableSortedSet.copyOf(
-          PresortedComparable<DexType>::slowCompareTo, enqueuer.instantiatedAnnotations);
+      ImmutableSortedSet.Builder<DexType> builder =
+          ImmutableSortedSet.orderedBy(PresortedComparable<DexType>::slowCompareTo);
+      enqueuer.instantiatedAnnotations.items.forEach(
+          annotation -> builder.add(annotation.annotation.type));
+      this.instantiatedAnnotations = builder.build();
       this.instantiatedTypes = ImmutableSortedSet.copyOf(
           PresortedComparable<DexType>::slowCompareTo, enqueuer.instantiatedTypes.getItems());
       this.instantiatedLambdas =
@@ -2795,6 +2803,14 @@
     registerEdge(getClassGraphNode(type), reason);
   }
 
+  private void registerAnnotation(DexAnnotation annotation, KeepReason reason) {
+    assert getSourceNode(reason) != null;
+    if (keptGraphConsumer == null) {
+      return;
+    }
+    registerEdge(getAnnotationGraphNode(annotation.annotation.type), reason);
+  }
+
   private void registerMethod(DexEncodedMethod method, KeepReason reason) {
     if (reason.edgeKind() == EdgeKind.IsLibraryMethod) {
       // Don't report edges to actual library methods.
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index a797742..2dd52eb 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo;
 import com.android.tools.r8.experimental.graphinfo.GraphEdgeInfo.EdgeKind;
 import com.android.tools.r8.experimental.graphinfo.GraphNode;
+import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexType;
@@ -17,6 +18,10 @@
 
   public abstract GraphNode getSourceNode(Enqueuer enqueuer);
 
+  static KeepReason annotatedOn(DexDefinition definition) {
+    return new AnnotatedOn(definition);
+  }
+
   static KeepReason dueToKeepRule(ProguardKeepRule rule) {
     return new DueToKeepRule(rule);
   }
@@ -302,6 +307,32 @@
     }
   }
 
+  private static class AnnotatedOn extends KeepReason {
+
+    private final DexDefinition holder;
+
+    private AnnotatedOn(DexDefinition holder) {
+      this.holder = holder;
+    }
+
+    @Override
+    public EdgeKind edgeKind() {
+      return EdgeKind.AnnotatedOn;
+    }
+
+    @Override
+    public GraphNode getSourceNode(Enqueuer enqueuer) {
+      if (holder.isDexClass()) {
+        return enqueuer.getClassGraphNode(holder.asDexClass().type);
+      } else if (holder.isDexEncodedField()) {
+        return enqueuer.getFieldGraphNode(holder.asDexEncodedField().field);
+      } else {
+        assert holder.isDexEncodedMethod();
+        return enqueuer.getMethodGraphNode(holder.asDexEncodedMethod().method);
+      }
+    }
+  }
+
   private static class ReflectiveUseFrom extends BasedOnOtherMethod {
 
     private ReflectiveUseFrom(DexEncodedMethod method) {
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 6d34f44..5b5cf9d 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -104,7 +104,7 @@
   private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
 
   // Parent dir for on-the-fly compiled jctf dex output.
-  private static final String JCTF_TESTS_PREFIX = "build/classes/jctfTests";
+  private static final String JCTF_TESTS_PREFIX = "build/classes/java/jctfTests";
   private static final String JCTF_TESTS_LIB_PREFIX =
       JCTF_TESTS_PREFIX + "/com/google/jctf/test/lib";
   private static final String JUNIT_TEST_RUNNER = "org.junit.runner.JUnitCore";
diff --git a/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
index ddd4f69..a1cfe7b 100644
--- a/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
+++ b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
@@ -19,7 +19,7 @@
     ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
     InstructionFactory factory = new InstructionFactory();
     Instruction[] instructions =
-        factory.readSequenceFrom(emptyBuffer, 0, 0, new OffsetToObjectMapping());
+        factory.readSequenceFrom(emptyBuffer.asShortBuffer(), 0, 0, new OffsetToObjectMapping());
     assertTrue(instructions.length == 0);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
new file mode 100644
index 0000000..f8ef59e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ObjectsRequireNonNullTest.java
@@ -0,0 +1,139 @@
+// 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 static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Streams;
+import java.util.Objects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+class ObjectsRequireNonNullTestMain {
+
+  static class Foo {
+    @NeverInline
+    void bar() {
+      System.out.println("Foo::bar");
+    }
+
+    @NeverInline
+    @Override
+    public String toString() {
+      return "Foo::toString";
+    }
+  }
+
+  @NeverInline
+  static void unknownArg(Foo foo) {
+    // It's unclear the argument is definitely null or not null.
+    Foo checked = Objects.requireNonNull(foo);
+    checked.bar();
+  }
+
+  public static void main(String[] args) {
+    Foo instance = new Foo();
+    // Not removable in debug mode.
+    Object nonNull = Objects.requireNonNull(instance);
+    System.out.println(nonNull);
+    // Removable because associated locals are changed while type casting.
+    Foo checked = Objects.requireNonNull(instance);
+    checked.bar();
+
+    unknownArg(instance);
+    try {
+      unknownArg(null);
+    } catch (NullPointerException npe) {
+      System.out.println("Expected NPE");
+    }
+  }
+}
+
+@RunWith(VmTestRunner.class)
+public class ObjectsRequireNonNullTest extends TestBase {
+  private static final String JAVA_OUTPUT = StringUtils.lines(
+      "Foo::toString",
+      "Foo::bar",
+      "Foo::bar",
+      "Expected NPE"
+  );
+  private static final Class<?> MAIN = ObjectsRequireNonNullTestMain.class;
+
+  @Test
+  public void testJvmOutput() throws Exception {
+    testForJvm().addTestClasspath().run(MAIN).assertSuccessWithOutput(JAVA_OUTPUT);
+  }
+
+  private static boolean isObjectsRequireNonNull(DexMethod method) {
+    return method.toSourceString().equals(
+        "java.lang.Object java.util.Objects.requireNonNull(java.lang.Object)");
+  }
+
+  private long countObjectsRequireNonNull(MethodSubject method) {
+    return Streams.stream(method.iterateInstructions(instructionSubject -> {
+      if (instructionSubject.isInvoke()) {
+        return isObjectsRequireNonNull(instructionSubject.getMethod());
+      }
+      return false;
+    })).count();
+  }
+
+  private void test(TestRunResult result, int expectedCount) throws Exception {
+    CodeInspector codeInspector = result.inspector();
+    ClassSubject mainClass = codeInspector.clazz(MAIN);
+    MethodSubject mainMethod = mainClass.mainMethod();
+    assertThat(mainMethod, isPresent());
+    long count = countObjectsRequireNonNull(mainMethod);
+    assertEquals(expectedCount, count);
+
+    MethodSubject unknownArg = mainClass.uniqueMethodWithName("unknownArg");
+    assertThat(unknownArg, isPresent());
+    // Due to the nullable argument, requireNonNull should remain.
+    assertEquals(1, countObjectsRequireNonNull(unknownArg));
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V4_4_4)
+  public void testD8() throws Exception {
+    TestRunResult result = testForD8()
+        .debug()
+        .addProgramClassesAndInnerClasses(MAIN)
+        .run(MAIN)
+        .assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 2);
+
+    result = testForD8()
+        .release()
+        .addProgramClassesAndInnerClasses(MAIN)
+        .run(MAIN)
+        .assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 0);
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V4_4_4)
+  public void testR8() throws Exception {
+    // CF disables move result optimization.
+    TestRunResult result = testForR8(Backend.DEX)
+        .addProgramClassesAndInnerClasses(MAIN)
+        .enableInliningAnnotations()
+        .addKeepMainRule(MAIN)
+        .run(MAIN)
+        .assertSuccessWithOutput(JAVA_OUTPUT);
+    test(result, 0);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
index cd3a131..7044366 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
@@ -35,7 +35,9 @@
         && "CONST".equalsIgnoreCase(arg)
         && "CONST".contentEquals(arg)
         && "CONST".indexOf(arg) > 0
-        && "CONST".lastIndexOf(arg) > 0;
+        && "CONST".lastIndexOf(arg) > 0
+        && "CONST".compareTo(arg) > 0
+        && "CONST".compareToIgnoreCase(arg) > 0;
   }
 
   public static void main(String[] args) {
@@ -52,6 +54,8 @@
       System.out.println(s1.indexOf("ix"));
       System.out.println(s1.lastIndexOf('f'));
       System.out.println(s1.lastIndexOf("ix"));
+      System.out.println(s1.compareTo("prefix-CONST-suffix") == 0);
+      System.out.println(s1.compareToIgnoreCase("PREFIX-const-SUFFIX") == 0);
     }
 
     {
@@ -68,6 +72,8 @@
       System.out.println(s2.indexOf("ix"));
       System.out.println(s2.lastIndexOf('f'));
       System.out.println(s2.lastIndexOf("ix"));
+      System.out.println(s2.compareTo("prefix-CONST-suffix") == 0);
+      System.out.println(s2.compareToIgnoreCase("pre-con-suf") == 0);
     }
 
     {
@@ -90,15 +96,15 @@
       StringContentCheckTestMain.class
   );
   private static final String JAVA_OUTPUT = StringUtils.lines(
-      // s1, contains
+      // s1, contains(String)
       "true",
-      // s1, startsWith
+      // s1, startsWith(String)
       "true",
-      // s1, endsWith
+      // s1, endsWith(String)
       "true",
-      // s1, equals
+      // s1, equals(String)
       "true",
-      // s1, equalsIgnoreCase
+      // s1, equalsIgnoreCase(String)
       "true",
       // s1, contentEquals(CharSequence)
       "true",
@@ -112,15 +118,19 @@
       "16",
       // s1, lastIndexOf(String)
       "17",
-      // s2, contains
+      // s1, compareTo(String)
+      "true",
+      // s1, compareToIgnoreCase(String)
+      "true",
+      // s2, contains(String)
       "false",
-      // s2, startsWith
+      // s2, startsWith(String)
       "false",
-      // s2, endsWith
+      // s2, endsWith(String)
       "false",
-      // s2, equals
+      // s2, equals(String)
       "false",
-      // s2, equalsIgnoreCase
+      // s2, equalsIgnoreCase(String)
       "false",
       // s2, contentEquals(CharSequence)
       "false",
@@ -134,6 +144,10 @@
       "-1",
       // s2, lastIndexOf(String)
       "-1",
+      // s2, compareTo(String)
+      "false",
+      // s2, compareToIgnoreCase(String)
+      "false",
       // argCouldBeNull
       "false"
   );
@@ -166,7 +180,9 @@
             || method.name.toString().equals("equalsIgnoreCase")
             || method.name.toString().equals("contentEquals")
             || method.name.toString().equals("indexOf")
-            || method.name.toString().equals("lastIndexOf"));
+            || method.name.toString().equals("lastIndexOf")
+            || method.name.toString().equals("compareTo")
+            || method.name.toString().equals("compareToIgnoreCase"));
   }
 
   private long countStringContentChecker(MethodSubject method) {
@@ -190,7 +206,7 @@
         "boolean", "argCouldBeNull", ImmutableList.of("java.lang.String"));
     assertThat(argCouldBeNull, isPresent());
     // Because of nullable argument, all checkers should remain.
-    assertEquals(8, countStringContentChecker(argCouldBeNull));
+    assertEquals(10, countStringContentChecker(argCouldBeNull));
   }
 
   @Test
@@ -202,14 +218,14 @@
         .addProgramClasses(CLASSES)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 22);
+    test(result, 26);
 
     result = testForD8()
         .release()
         .addProgramClasses(CLASSES)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 12);
+    test(result, 14);
   }
 
   @Test
@@ -221,6 +237,6 @@
         .addKeepMainRule(MAIN)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 12);
+    test(result, 14);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index 877ffc0..4209cb6 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -312,11 +312,15 @@
     assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
     DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
     assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
-    // Due to package-private access, classes in the same package should remain as-is.
-    DexType ab = dexItemFactory.createType("Lnaming101/a/b;");
-    assertEquals("Lnaming101/a/b;", naming.lookupDescriptor(ab).toSourceString());
+    // Due to package-private access, classes in the same package should remain in the same package.
+    DexType ac = dexItemFactory.createType("Lnaming101/a/c;");
+    assertEquals(
+        getPackageNameFromDescriptor(naming.lookupDescriptor(aa).toString()),
+        getPackageNameFromDescriptor(naming.lookupDescriptor(ac).toString()));
     DexType bb = dexItemFactory.createType("Lnaming101/b/b;");
-    assertEquals("Lnaming101/b/b;", naming.lookupDescriptor(bb).toSourceString());
+    assertEquals(
+        getPackageNameFromDescriptor(naming.lookupDescriptor(ba).toString()),
+        getPackageNameFromDescriptor(naming.lookupDescriptor(bb).toString()));
 
     // All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
     // conflict. Thus, those should not be renamed to 'a'.
@@ -340,11 +344,15 @@
     assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
     DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
     assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
-    // Due to package-private access, classes in the same package should remain as-is.
-    DexType ab = dexItemFactory.createType("Lnaming101/a/b;");
-    assertEquals("Lnaming101/a/b;", naming.lookupDescriptor(ab).toSourceString());
+    // Due to package-private access, classes in the same package should remain in the same package.
+    DexType ac = dexItemFactory.createType("Lnaming101/a/c;");
+    assertEquals(
+        getPackageNameFromDescriptor(naming.lookupDescriptor(aa).toString()),
+        getPackageNameFromDescriptor(naming.lookupDescriptor(ac).toString()));
     DexType bb = dexItemFactory.createType("Lnaming101/b/b;");
-    assertEquals("Lnaming101/b/b;", naming.lookupDescriptor(bb).toSourceString());
+    assertEquals(
+        getPackageNameFromDescriptor(naming.lookupDescriptor(ba).toString()),
+        getPackageNameFromDescriptor(naming.lookupDescriptor(bb).toString()));
 
     // All other packages are flattened to naming101, hence all other classes will be in
     // naming101.* package. Due to naming101.a.a, prefix naming101.a is already used. So,
diff --git a/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTest.java b/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTest.java
index 6a03a43..d199f57 100644
--- a/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTest.java
@@ -4,16 +4,21 @@
 
 package com.android.tools.r8.shaking;
 
+import static junit.framework.TestCase.assertTrue;
+
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.io.IOException;
-import org.junit.Ignore;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 class UnusedTypeInThrowing {
 
+  public static final String EXPECTED = "42";
+
   public static void main(String[] args) throws UnusedTypeInThrowingThrowable {
-    System.out.print("42");
+    System.out.print(EXPECTED);
   }
 }
 
@@ -25,14 +30,20 @@
   static final Class MAIN_CLASS = UnusedTypeInThrowing.class;
 
   @Test
-  @Ignore("b/124019003")
-  public void testTypeIsMarkedAsLive() throws IOException, CompilationFailedException {
-    testForR8(Backend.CF)
-        .addProgramClasses(MAIN_CLASS)
-        .addProgramClasses(THROWABLE_CLASS)
-        .addKeepMainRule(MAIN_CLASS)
-        .addKeepRules("-keepattributes Exceptions")
-        .run(MAIN_CLASS)
-        .assertSuccessWithOutput("42");
+  public void testTypeIsMarkedAsLive()
+      throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException {
+    CodeInspector inspector =
+        testForR8(Backend.CF)
+            .enableGraphInspector()
+            .addProgramClasses(MAIN_CLASS)
+            .addProgramClasses(THROWABLE_CLASS)
+            .addKeepMainRule(MAIN_CLASS)
+            .addKeepRules("-keepattributes Exceptions")
+            .run(MAIN_CLASS)
+            .assertSuccessWithOutput(UnusedTypeInThrowing.EXPECTED)
+            .inspector();
+
+    assertTrue(inspector.clazz(THROWABLE_CLASS).isPresent());
+    // TODO(b/124217402) When done check that THROWABLE_CLASS is kept by the throwing annotation.
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTestRunner.java b/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTestRunner.java
deleted file mode 100644
index 2bf42a1..0000000
--- a/src/test/java/com/android/tools/r8/shaking/UnusedTypeInThrowingTestRunner.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.shaking;
-
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.TestBase;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.nio.file.Path;
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class UnusedTypeInThrowingTestRunner extends TestBase {
-
-  static final Class THROWABLE_CLASS = UnusedTypeInThrowingThrowable.class;
-  static final Class MAIN_CLASS = UnusedTypeInThrowingTest.class;
-
-  @Test
-  @Ignore("b/124019003")
-  public void testTypeIsMarkedAsLive() throws IOException, CompilationFailedException {
-    Path outDex = temp.newFile("out.zip").toPath();
-    testForR8(Backend.CF)
-        .addProgramClasses(MAIN_CLASS)
-        .addProgramClasses(THROWABLE_CLASS)
-        .addKeepMainRule(MAIN_CLASS)
-        .addKeepRules(ImmutableList.of("-keepattributes Exceptions"))
-        .setMode(CompilationMode.RELEASE)
-        .enableInliningAnnotations()
-        .minification(true)
-        .compile()
-        .run(MAIN_CLASS)
-        .assertSuccessWithOutput("42");
-  }
-}
diff --git a/tools/api_sample_coverage.py b/tools/api_sample_coverage.py
index b14ad1a..4eea8c7 100755
--- a/tools/api_sample_coverage.py
+++ b/tools/api_sample_coverage.py
@@ -8,6 +8,7 @@
 '''
 
 import argparse
+import jdk
 import os
 import subprocess
 import utils
@@ -23,16 +24,17 @@
   if output_dir is None:
     output_dir = ''
 
+  javaExecutable = jdk.GetJavaExecutable()
   printseeds_path = os.path.join(output_dir, 'keep-seeds.txt')
   printseeds_args = [
-    'java', '-jar', utils.R8_JAR, 'printseeds',
+    javaExecutable, '-jar', utils.R8_JAR, 'printseeds',
     utils.RT_JAR, utils.R8_JAR, utils.R8LIB_KEEP_RULES,
   ]
   write_sorted_lines(printseeds_args, printseeds_path)
 
   printuses_path = os.path.join(output_dir, 'sample-uses.txt')
   printuses_args = [
-    'java', '-jar', utils.R8_JAR, 'printuses',
+    javaExecutable, '-jar', utils.R8_JAR, 'printuses',
     utils.RT_JAR, utils.R8_JAR, API_SAMPLE_JAR,
   ]
   write_sorted_lines(printuses_args, printuses_path)
diff --git a/tools/archive.py b/tools/archive.py
index d8fb4d0..9de199c 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -5,6 +5,7 @@
 
 import create_maven_release
 import gradle
+import jdk
 import optparse
 import os
 import shutil
@@ -25,7 +26,9 @@
   return result.parse_args()
 
 def GetToolVersion(jar_path):
-  output = subprocess.check_output(['java', '-jar', jar_path, '--version'])
+  output = subprocess.check_output([
+    jdk.GetJavaExecutable(), '-jar', jar_path, '--version'
+  ])
   return output.splitlines()[0].strip()
 
 def GetVersion():
diff --git a/tools/asmifier.py b/tools/asmifier.py
index 280c6f1..3080378 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -4,6 +4,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 import gradle
+import jdk
 import os
 import subprocess
 import sys
@@ -13,7 +14,7 @@
   if build:
     gradle.RunGradle(['copyMavenDeps'])
   cmd = []
-  cmd.append('java')
+  cmd.append(jdk.GetJavaExecutable())
   cp = ":".join([os.path.join(utils.REPO_ROOT, 'build/deps/asm-6.2.1.jar'),
                  os.path.join(utils.REPO_ROOT, 'build/deps/asm-util-6.2.1.jar')])
   cmd.extend(['-cp', cp])
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
index 5368f6f..980715e 100755
--- a/tools/build_r8lib.py
+++ b/tools/build_r8lib.py
@@ -10,6 +10,7 @@
 
 import argparse
 import gradle
+import jdk
 import os
 import subprocess
 import toolhelper
@@ -76,7 +77,8 @@
 
 def test_d8sample(paths):
   with utils.TempDir() as path:
-    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
+    args = [jdk.GetJavaExecutable(),
+            '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
             'com.android.tools.apiusagesample.D8ApiUsageSample',
             '--output', path,
             '--min-api', str(API_LEVEL),
@@ -94,7 +96,8 @@
     # R8CommandParser should have been minified in LIB_JAR.
     # Just in case R8CommandParser is also present in LIB_JAR, we put
     # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
-    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
+    args = [jdk.GetJavaExecutable(),
+            '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
             'com.android.tools.r8.R8CommandParser',
             '--output', path + "/output.zip",
             '--min-api', str(API_LEVEL),
@@ -111,7 +114,8 @@
     # R8CommandParser should have been minified in LIB_JAR.
     # Just in case R8CommandParser is also present in LIB_JAR, we put
     # SAMPLE_JAR first on the classpath to use its version of R8CommandParser.
-    args = ['java', '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
+    args = [jdk.GetJavaExecutable(),
+            '-cp', '%s:%s' % (SAMPLE_JAR, ":".join(paths)),
             'com.android.tools.r8.R8CommandParser',
             '--classfile',
             '--output', path + "/output.jar",
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index 9053e23..29f7459 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -8,6 +8,7 @@
 import apk_utils
 import fnmatch
 import glob
+import jdk
 import optparse
 import os
 import shutil
@@ -21,7 +22,7 @@
 DEFAULT_AAPT = 'aapt' # Assume in path.
 DEFAULT_D8 = os.path.join(utils.REPO_ROOT, 'tools', 'd8.py')
 DEFAULT_DEXSPLITTER = os.path.join(utils.REPO_ROOT, 'tools', 'dexsplitter.py')
-DEFAULT_JAVAC = 'javac'
+DEFAULT_JAVAC = jdk.GetJavacExecutable()
 SRC_LOCATION = 'src/com/android/tools/r8/sample/{app}/*.java'
 DEFAULT_KEYSTORE = os.path.join(os.getenv('HOME'), '.android', 'debug.keystore')
 PACKAGE_PREFIX = 'com.android.tools.r8.sample'
diff --git a/tools/defines.py b/tools/defines.py
new file mode 100644
index 0000000..6fdd24f
--- /dev/null
+++ b/tools/defines.py
@@ -0,0 +1,22 @@
+# 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.
+
+import os
+import sys
+
+# Static defines for use in tools (and utils.py).
+# There must be no dependencies on other utils from this file!
+
+TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
+REPO_ROOT = os.path.realpath(os.path.join(TOOLS_DIR, '..'))
+THIRD_PARTY = os.path.join(REPO_ROOT, 'third_party')
+
+def IsWindows():
+  return sys.platform.startswith('win')
+
+def IsLinux():
+  return sys.platform.startswith('linux')
+
+def IsOsX():
+  return sys.platform.startswith('darwin')
\ No newline at end of file
diff --git a/tools/gradle.py b/tools/gradle.py
index 374f885..232e96d 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -44,9 +44,15 @@
   utils.EnsureDepFromGoogleCloudStorage(
     GRADLE, GRADLE_TGZ, GRADLE_SHA1, 'Gradle binary')
 
+def EnsureJdk():
+  jdkHome = jdk.GetJdkHome()
+  jdkTgz = jdkHome + '.tar.gz'
+  jdkSha1 = jdkTgz + '.sha1'
+  utils.EnsureDepFromGoogleCloudStorage(jdkHome, jdkTgz, jdkSha1, 'JDK')
+
 def EnsureDeps():
   EnsureGradle()
-  jdk.EnsureJdk()
+  EnsureJdk()
 
 def RunGradleIn(gradleCmd, args, cwd, throw_on_failure=True, env=None):
   EnsureDeps()
diff --git a/tools/jdk.py b/tools/jdk.py
index 5a20901..5791e50 100755
--- a/tools/jdk.py
+++ b/tools/jdk.py
@@ -3,40 +3,33 @@
 # 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.
 
+import defines
 import os
 import sys
-import utils
 
-JDK_DIR = os.path.join(utils.REPO_ROOT, 'third_party', 'openjdk')
+JDK_DIR = os.path.join(defines.THIRD_PARTY, 'openjdk')
 
 def GetJdkHome():
   root = os.path.join(JDK_DIR, 'openjdk-9.0.4')
-  if utils.IsLinux():
+  if defines.IsLinux():
     return os.path.join(root, 'linux')
-  elif utils.IsOsX():
+  elif defines.IsOsX():
     return os.path.join(root, 'osx')
-  elif utils.IsWindows():
+  elif defines.IsWindows():
     return os.path.join(root, 'windows')
   else:
     return os.environ['JAVA_HOME']
-  return jdkHome
 
 def GetJavaExecutable(jdkHome=None):
   jdkHome = jdkHome if jdkHome else GetJdkHome()
-  executable = 'java.exe' if utils.IsWindows() else 'java'
+  executable = 'java.exe' if defines.IsWindows() else 'java'
   return os.path.join(jdkHome, 'bin', executable) if jdkHome else executable
 
 def GetJavacExecutable(jdkHome=None):
   jdkHome = jdkHome if jdkHome else GetJdkHome()
-  executable = 'javac.exe' if utils.IsWindows() else 'javac'
+  executable = 'javac.exe' if defines.IsWindows() else 'javac'
   return os.path.join(jdkHome, 'bin', executable) if jdkHome else executable
 
-def EnsureJdk():
-  jdkHome = GetJdkHome()
-  jdkTgz = jdkHome + '.tar.gz'
-  jdkSha1 = jdkTgz + '.sha1'
-  utils.EnsureDepFromGoogleCloudStorage(jdkHome, jdkTgz, jdkSha1, 'JDK')
-
 def Main():
   print GetJdkHome()
 
diff --git a/tools/proguard.py b/tools/proguard.py
index 640094f..b1bde54 100755
--- a/tools/proguard.py
+++ b/tools/proguard.py
@@ -6,6 +6,7 @@
 # Run ProGuard, Google's internal version
 
 from __future__ import print_function
+import jdk
 import os
 import subprocess
 import sys
@@ -19,7 +20,7 @@
   cmd = []
   if track_memory_file:
     cmd.extend(['tools/track_memory.sh', track_memory_file])
-  cmd.extend(['java', '-jar', PROGUARD_JAR])
+  cmd.extend([jdk.GetJavaExecutable(), '-jar', PROGUARD_JAR])
   cmd.extend(args)
   utils.PrintCmd(cmd)
   subprocess.call(cmd, stdout=stdout, stderr=stderr)
diff --git a/tools/retrace.py b/tools/retrace.py
index 1b77e9a..44120f2 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -5,6 +5,7 @@
 
 import archive
 import argparse
+import jdk
 import subprocess
 import sys
 import tempfile
@@ -49,7 +50,9 @@
       print('Could not find map file from argument: %s.' % hashOrVersion)
       return 1
 
-  retrace_args = ['java', '-jar', utils.RETRACE_JAR, r8lib_map_path]
+  retrace_args = [
+    jdk.GetJavaExecutable(), '-jar', utils.RETRACE_JAR, r8lib_map_path
+  ]
   if args.stacktrace:
     retrace_args.append(args.stacktrace)
 
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 2889d74..506c583 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -7,6 +7,7 @@
 import apk_utils
 import golem
 import gradle
+import jdk
 import os
 import optparse
 import shutil
@@ -177,7 +178,7 @@
 
   # Use the copy of r8.jar if it is there.
   if os.path.isfile(r8_jar):
-    cmd = ['java', '-ea', '-jar', r8_jar, 'extractmarker', apk]
+    cmd = [jdk.GetJavaExecutable(), '-ea', '-jar', r8_jar, 'extractmarker', apk]
   else:
     script = os.path.join(utils.TOOLS_DIR, 'extractmarker.py')
     cmd = ['python', script, apk]
@@ -493,7 +494,7 @@
   # is 'r8'.
   entry_point = 'com.android.tools.r8.R8'
 
-  cmd = ['java', '-ea:com.android.tools.r8...', '-cp', r8_jar, entry_point,
+  cmd = [jdk.GetJavaExecutable(), '-ea:com.android.tools.r8...', '-cp', r8_jar, entry_point,
       '--release', '--min-api', str(min_sdk), '--pg-conf', proguard_config_file,
       '--lib', android_jar, '--output', zip_dest, apk]
 
diff --git a/tools/run_proguard_dx_on_app.py b/tools/run_proguard_dx_on_app.py
index e78d5d1..4e408ff 100755
--- a/tools/run_proguard_dx_on_app.py
+++ b/tools/run_proguard_dx_on_app.py
@@ -13,6 +13,7 @@
 import argparse
 import fnmatch
 import gmscore_data
+import jdk
 import os
 import stat
 import sys
@@ -140,7 +141,7 @@
     if options.print_memoryuse:
       track_memory_file = join(temp, utils.MEMORY_USE_TMP_FILE)
       cmd.extend(['tools/track_memory.sh', track_memory_file])
-    cmd.extend(['java', '-jar', jar, '--multi-dex',
+    cmd.extend([jdk.GetJavaExecutable(), '-jar', jar, '--multi-dex',
         '--output=' + outdir])
     if 'min-api' in values_proguarded:
       cmd.append('--min-sdk-version=' + values_proguarded['min-api'])
diff --git a/tools/test_aosp_jar.py b/tools/test_aosp_jar.py
index bf12045..6f7e8f5 100755
--- a/tools/test_aosp_jar.py
+++ b/tools/test_aosp_jar.py
@@ -10,6 +10,7 @@
 from itertools import chain
 from os.path import join
 import argparse
+import jdk
 import os
 import subprocess
 import sys
@@ -36,7 +37,7 @@
   if not args.no_build:
     gradle.RunGradle(['CompatDx'])
 
-  cmd = [REPLAY_SCRIPT, 'java', '-jar', utils.COMPATDX_JAR]
+  cmd = [REPLAY_SCRIPT, jdk.GetJavaExecutable(), '-jar', utils.COMPATDX_JAR]
   utils.PrintCmd(cmd)
   subprocess.check_call(cmd)
 
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 7400541..f30d7d9 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -21,6 +21,7 @@
 from glob import glob
 import argparse
 import golem
+import jdk
 import os
 import re
 import subprocess
@@ -96,7 +97,7 @@
 
     if tool_file.endswith('.jar'):
       assert xmx is not None
-      cmd.extend(['java', xmx, '-jar'])
+      cmd.extend([jdk.GetJavaExecutable(), xmx, '-jar'])
 
     cmd.extend([tool_file] + tool_args + [FRAMEWORK_JAR])
 
diff --git a/tools/test_self_retrace.py b/tools/test_self_retrace.py
index 6e48aa6..46cb1d9 100755
--- a/tools/test_self_retrace.py
+++ b/tools/test_self_retrace.py
@@ -7,6 +7,7 @@
 from __future__ import division
 from __future__ import print_function
 import gradle
+import jdk
 import os
 import subprocess
 import sys
@@ -37,7 +38,9 @@
     raise Exception("Only one argument is allowed, see '--help'.")
 
   # Run 'r8 --help' which throws an exception.
-  cmd = ['java','-cp', r8lib, 'com.android.tools.r8.R8', '--help']
+  cmd = [
+    jdk.GetJavaExecutable(),'-cp', r8lib, 'com.android.tools.r8.R8', '--help'
+  ]
   os.environ["R8_THROW_EXCEPTION_FOR_TESTING_RETRACE"] = "1"
   utils.PrintCmd(cmd)
   p = subprocess.Popen(cmd, stderr=subprocess.PIPE)
@@ -48,7 +51,7 @@
   assert('SelfRetraceTest' not in stacktrace)
 
   # Run the retrace tool.
-  cmd = ['java', '-jar', utils.RETRACE_JAR, r8lib + ".map"]
+  cmd = [jdk.GetJavaExecutable(), '-jar', utils.RETRACE_JAR, r8lib + ".map"]
   utils.PrintCmd(cmd)
   p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
   retrace_stdout, _ = p.communicate(stacktrace)
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 9371fb9..10da97d 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -4,7 +4,7 @@
 
 import glob
 import gradle
-import os
+import jdk
 import subprocess
 from threading import Timer
 import utils
@@ -19,7 +19,7 @@
   cmd = []
   if track_memory_file:
     cmd.extend(['tools/track_memory.sh', track_memory_file])
-  cmd.append('java')
+  cmd.append(jdk.GetJavaExecutable())
   if extra_args:
     cmd.extend(extra_args)
   if debug:
@@ -61,7 +61,7 @@
       'compileTestJava',
     ])
   cmd = []
-  cmd.append('java')
+  cmd.append(jdk.GetJavaExecutable())
   if extra_args:
     cmd.extend(extra_args)
   if debug:
diff --git a/tools/utils.py b/tools/utils.py
index 20d7bc7..892fe7d 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -4,7 +4,9 @@
 
 # Different utility functions used accross scripts
 
+import defines
 import hashlib
+import jdk
 import os
 import re
 import shutil
@@ -16,9 +18,9 @@
 
 ANDROID_JAR_DIR = 'third_party/android_jar/lib-v{api}'
 ANDROID_JAR = os.path.join(ANDROID_JAR_DIR, 'android.jar')
-TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
-REPO_ROOT = os.path.realpath(os.path.join(TOOLS_DIR, '..'))
-THIRD_PARTY = os.path.join(REPO_ROOT, 'third_party')
+TOOLS_DIR = defines.TOOLS_DIR
+REPO_ROOT = defines.REPO_ROOT
+THIRD_PARTY = defines.THIRD_PARTY
 MEMORY_USE_TMP_FILE = 'memory_use.tmp'
 DEX_SEGMENTS_RESULT_PATTERN = re.compile('- ([^:]+): ([0-9]+)')
 BUILD = os.path.join(REPO_ROOT, 'build')
@@ -156,13 +158,13 @@
       return stdout
 
 def IsWindows():
-  return sys.platform.startswith('win')
+  return defines.IsWindows()
 
 def IsLinux():
-  return sys.platform.startswith('linux')
+  return defines.IsLinux()
 
 def IsOsX():
-  return sys.platform.startswith('darwin')
+  return defines.IsOsX()
 
 def EnsureDepFromGoogleCloudStorage(dep, tgz, sha1, msg):
   if not os.path.exists(dep) or os.path.getmtime(tgz) < os.path.getmtime(sha1):
@@ -385,7 +387,7 @@
 # Return a dictionary: {segment_name -> segments_size}
 def getDexSegmentSizes(dex_files):
   assert len(dex_files) > 0
-  cmd = ['java', '-jar', R8_JAR, 'dexsegments']
+  cmd = [jdk.GetJavaExecutable(), '-jar', R8_JAR, 'dexsegments']
   cmd.extend(dex_files)
   PrintCmd(cmd)
   output = subprocess.check_output(cmd)
@@ -405,7 +407,7 @@
 
 # Return a dictionary: {segment_name -> segments_size}
 def getCfSegmentSizes(cfFile):
-  cmd = ['java',
+  cmd = [jdk.GetJavaExecutable(),
          '-cp',
          CF_SEGMENTS_TOOL,
          'com.android.tools.r8.cf_segments.MeasureLib',
@@ -443,7 +445,7 @@
 
 # Ensure that we are not benchmarking with a google jvm.
 def check_java_version():
-  cmd= ['java', '-version']
+  cmd= [jdk.GetJavaExecutable(), '-version']
   output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
   m = re.search('openjdk version "([^"]*)"', output)
   if m is None: