Merge "Upgrade kotlinx metadata to 0.0.3"
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 409c437..b7fc6b3 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 = "1.3.6-dev";
+  public static final String LABEL = "1.3.7-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 5bed061..8ab2c1a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
 
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -467,7 +468,7 @@
         return instruction;
       }
     }
-    return null;
+    throw new Unreachable();
   }
 
   public void clearUserInfo() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index fffbc44..c6ba97d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -46,7 +46,6 @@
 import com.android.tools.r8.ir.optimize.NonNullTracker;
 import com.android.tools.r8.ir.optimize.Outliner;
 import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
-import com.android.tools.r8.ir.optimize.RedundantFieldLoadElimination;
 import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
 import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
@@ -696,7 +695,6 @@
     codeRewriter.rewriteSwitch(code);
     codeRewriter.processMethodsNeverReturningNormally(code);
     codeRewriter.simplifyIf(code, typeEnvironment);
-    new RedundantFieldLoadElimination(code).run();
 
     if (options.testing.invertConditionals) {
       invertConditionalsForTesting(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
deleted file mode 100644
index 24012b1..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2018, 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 com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.DominatorTree;
-import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.Value;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Eliminate redundant field loads.
- *
- * <p>Simple algorithm that goes through all blocks in one pass in dominator order and propagates
- * active field sets across control-flow edges where the target has only one predecessor.
- */
-// TODO(ager): Evaluate speed/size for computing active field sets in a fixed-point computation.
-public class RedundantFieldLoadElimination {
-
-  private final IRCode code;
-  private final DominatorTree dominatorTree;
-
-  // Maps keeping track of fields that have an already loaded value at basic block entry.
-  private final HashMap<BasicBlock, HashMap<FieldAndObject, Instruction>>
-      activeInstanceFieldsAtEntry = new HashMap<>();
-  private final HashMap<BasicBlock, HashMap<DexField, Instruction>> activeStaticFieldsAtEntry =
-      new HashMap<>();
-
-  // Maps keeping track of fields with already loaded values for the current block during
-  // elimination.
-  private HashMap<FieldAndObject, Instruction> activeInstanceFields;
-  private HashMap<DexField, Instruction> activeStaticFields;
-
-  public RedundantFieldLoadElimination(IRCode code) {
-    this.code = code;
-    dominatorTree = new DominatorTree(code);
-  }
-
-  private static class FieldAndObject {
-    private final DexField field;
-    private final Value object;
-
-    private FieldAndObject(DexField field, Value receiver) {
-      this.field = field;
-      this.object = receiver;
-    }
-
-    @Override
-    public int hashCode() {
-      return field.hashCode() * 7 + object.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-      if (!(other instanceof FieldAndObject)) {
-        return false;
-      }
-      FieldAndObject o = (FieldAndObject) other;
-      return o.object == object && o.field == field;
-    }
-  }
-
-  public void run() {
-    for (BasicBlock block : dominatorTree.getSortedBlocks()) {
-      activeInstanceFields =
-          activeInstanceFieldsAtEntry.containsKey(block)
-              ? activeInstanceFieldsAtEntry.get(block)
-              : new HashMap<>();
-      activeStaticFields =
-          activeStaticFieldsAtEntry.containsKey(block)
-              ? activeStaticFieldsAtEntry.get(block)
-              : new HashMap<>();
-      InstructionListIterator it = block.listIterator();
-      while (it.hasNext()) {
-        Instruction instruction = it.next();
-        if (instruction.isFieldInstruction()) {
-          DexField field = instruction.asFieldInstruction().getField();
-          if (instruction.isInstancePut() || instruction.isStaticPut()) {
-            killActiveFields(instruction.asFieldInstruction());
-          } else if (instruction.isInstanceGet() && !instruction.outValue().hasLocalInfo()) {
-            Value object = instruction.asInstanceGet().object();
-            FieldAndObject fieldAndObject = new FieldAndObject(field, object);
-            if (activeInstanceFields.containsKey(fieldAndObject)) {
-              Instruction active = activeInstanceFields.get(fieldAndObject);
-              eliminateRedundantRead(it, instruction, active);
-            } else {
-              activeInstanceFields.put(fieldAndObject, instruction);
-            }
-          } else if (instruction.isStaticGet() && !instruction.outValue().hasLocalInfo()) {
-            if (activeStaticFields.containsKey(field)) {
-              Instruction active = activeStaticFields.get(field);
-              eliminateRedundantRead(it, instruction, active);
-            } else {
-              // A field get on a different class can cause <clinit> to run and change static
-              // field values.
-              killActiveFields(instruction.asFieldInstruction());
-              activeStaticFields.put(field, instruction);
-            }
-          }
-        }
-        if (instruction.isMonitor() || instruction.isInvokeMethod()) {
-          activeInstanceFields.clear();
-          activeStaticFields.clear();
-        }
-      }
-      propagateActiveFieldsFrom(block);
-    }
-    assert code.isConsistentSSA();
-  }
-
-  private void propagateActiveFieldsFrom(BasicBlock block) {
-    for (BasicBlock successor : block.getSuccessors()) {
-      // Allow propagation across exceptional edges, just be careful not to propagate if the
-      // throwing instruction is a field instruction.
-      if (successor.getPredecessors().size() == 1) {
-        if (block.hasCatchSuccessor(successor)) {
-          Instruction exceptionalExit = block.exceptionalExit();
-          if (exceptionalExit != null && exceptionalExit.isFieldInstruction()) {
-            killActiveFieldsForExceptionalExit(exceptionalExit.asFieldInstruction());
-          }
-        }
-        assert !activeInstanceFieldsAtEntry.containsKey(successor);
-        activeInstanceFieldsAtEntry.put(successor, new HashMap<>(activeInstanceFields));
-        assert !activeStaticFieldsAtEntry.containsKey(successor);
-        activeStaticFieldsAtEntry.put(successor, new HashMap<>(activeStaticFields));
-      }
-    }
-  }
-
-  private void killActiveFields(FieldInstruction instruction) {
-    DexField field = instruction.getField();
-    if (instruction.isInstancePut()) {
-      // Remove all the field/object pairs that refer to this field to make sure
-      // that we are conservative.
-      List<FieldAndObject> keysToRemove = new ArrayList<>();
-      for (FieldAndObject key : activeInstanceFields.keySet()) {
-        if (key.field == field) {
-          keysToRemove.add(key);
-        }
-      }
-      keysToRemove.forEach((k) -> activeInstanceFields.remove(k));
-    } else if (instruction.isInstanceGet()) {
-      Value object = instruction.asInstanceGet().object();
-      FieldAndObject fieldAndObject = new FieldAndObject(field, object);
-      activeInstanceFields.remove(fieldAndObject);
-    } else if (instruction.isStaticPut()) {
-      if (field.clazz != code.method.method.holder) {
-        // Accessing a static field on a different object could cause <clinit> to run which
-        // could modify any static field on any other object.
-        activeStaticFields.clear();
-      } else {
-        activeStaticFields.remove(field);
-      }
-    } else if (instruction.isStaticGet()) {
-      if (field.clazz != code.method.method.holder) {
-        // Accessing a static field on a different object could cause <clinit> to run which
-        // could modify any static field on any other object.
-        activeStaticFields.clear();
-      }
-    }
-  }
-
-  // If a field get instruction throws an exception it did not have an effect on the
-  // value of the field. Therefore, when propagating across exceptional edges for a
-  // field get instruction we have to exclude that field from the set of known
-  // field values.
-  private void killActiveFieldsForExceptionalExit(FieldInstruction instruction) {
-    DexField field = instruction.getField();
-    if (instruction.isInstanceGet()) {
-      Value object = instruction.asInstanceGet().object();
-      FieldAndObject fieldAndObject = new FieldAndObject(field, object);
-      activeInstanceFields.remove(fieldAndObject);
-    } else if (instruction.isStaticGet()) {
-      activeStaticFields.remove(field);
-    }
-  }
-
-  private void eliminateRedundantRead(
-      InstructionListIterator it, Instruction redundant, Instruction active) {
-    redundant.outValue().replaceUsers(active.outValue());
-    it.removeOrReplaceByDebugLocalRead();
-    active.outValue().uniquePhiUsers().forEach(Phi::removeTrivialPhi);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 581ac8a..b0f4dc0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -6,7 +6,7 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.inferMemberOrTypeFromNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
 
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -32,7 +32,6 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Streams;
@@ -82,7 +81,7 @@
   }
 
   public void decoupleIdentifierNameStringsInMethod(DexEncodedMethod encodedMethod, IRCode code) {
-    Origin origin = appInfo.originFor(code.method.method.getHolder());
+    DexType originHolder = code.method.method.getHolder();
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -110,17 +109,15 @@
               ? instruction.asStaticPut().inValue()
               : instruction.asInstancePut().value();
           if (!in.isConstString()) {
-            if (options.proguardConfiguration.isObfuscating()) {
-              warnUndeterminedIdentifier(options.reporter, field, origin, instruction, null);
-            }
+            warnUndeterminedIdentifierIfNecessary(
+                appInfo, options, field, originHolder, instruction, null);
             continue;
           }
           DexString original = in.getConstInstruction().asConstString().getValue();
           DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
           if (itemBasedString == null) {
-            if (options.proguardConfiguration.isObfuscating()) {
-              warnUndeterminedIdentifier(options.reporter, field, origin, instruction, original);
-            }
+            warnUndeterminedIdentifierIfNecessary(
+                appInfo, options, field, originHolder, instruction, original);
             continue;
           }
           // Move the cursor back to $fieldPut
@@ -174,10 +171,8 @@
           if (isReflectionMethod(dexItemFactory, invokedMethod)) {
             DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
             if (itemBasedString == null) {
-              if (options.proguardConfiguration.isObfuscating()) {
-                warnUndeterminedIdentifier(
-                    options.reporter, invokedMethod, origin, instruction, null);
-              }
+              warnUndeterminedIdentifierIfNecessary(
+                  appInfo, options, invokedMethod, originHolder, instruction, null);
               continue;
             }
             DexType returnType = invoke.getReturnType();
@@ -222,20 +217,16 @@
             for (int i = 0; i < ins.size(); i++) {
               Value in = ins.get(i);
               if (!in.isConstString()) {
-                if (options.proguardConfiguration.isObfuscating()) {
-                  warnUndeterminedIdentifier(
-                      options.reporter, invokedMethod, origin, instruction, null);
-                }
+                warnUndeterminedIdentifierIfNecessary(
+                    appInfo, options, invokedMethod, originHolder, instruction, null);
                 continue;
               }
               DexString original = in.getConstInstruction().asConstString().getValue();
               DexItemBasedString itemBasedString =
                   inferMemberOrTypeFromNameString(appInfo, original);
               if (itemBasedString == null) {
-                if (options.proguardConfiguration.isObfuscating()) {
-                  warnUndeterminedIdentifier(
-                      options.reporter, invokedMethod, origin, instruction, original);
-                }
+                warnUndeterminedIdentifierIfNecessary(
+                    appInfo, options, invokedMethod, originHolder, instruction, original);
                 continue;
               }
               // Move the cursor back to $invoke
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 471b3f8..bf23f59 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -26,7 +26,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.TextPosition;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import java.util.List;
@@ -372,13 +372,24 @@
     return new DexTypeList(types);
   }
 
-  public static void warnUndeterminedIdentifier(
-      Reporter reporter,
+  public static void warnUndeterminedIdentifierIfNecessary(
+      AppInfo appInfo,
+      InternalOptions options,
       DexItem member,
-      Origin origin,
+      DexType originHolder,
       Instruction instruction,
       DexString original) {
     assert member instanceof DexField || member instanceof DexMethod;
+    DexClass originClass = appInfo.definitionFor(originHolder);
+    // If the origin is a library class, it is out of developers' control.
+    if (originClass != null && originClass.isLibraryClass()) {
+      return;
+    }
+    // Undetermined identifiers matter only if minification is enabled.
+    if (!options.proguardConfiguration.isObfuscating()) {
+      return;
+    }
+    Origin origin = appInfo.originFor(originHolder);
     String kind = member instanceof DexField ? "field" : "method";
     String originalMessage = original == null ? "what identifier string flows to "
         : "what '" + original.toString() + "' refers to, which flows to ";
@@ -392,6 +403,6 @@
             ? new StringDiagnostic(message, origin,
                 new TextPosition(0L, instruction.getPosition().line, 1))
             : new StringDiagnostic(message, origin);
-    reporter.warning(diagnostic);
+    options.reporter.warning(diagnostic);
   }
 }
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 061b9bf..8335cba 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,7 +5,7 @@
 
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
 import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
 
 import com.android.tools.r8.Diagnostic;
@@ -1359,13 +1359,14 @@
   }
 
   private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
-    Origin origin = appInfo.originFor(method.method.holder);
+    DexType originHolder = method.method.holder;
+    Origin origin = appInfo.originFor(originHolder);
     IRCode code = method.buildIR(appInfo, options, origin);
     code.instructionIterator().forEachRemaining(instr ->
-        handleProguardReflectiveBehavior(instr, origin));
+        handleProguardReflectiveBehavior(instr, originHolder));
   }
 
-  private void handleProguardReflectiveBehavior(Instruction instruction, Origin origin) {
+  private void handleProguardReflectiveBehavior(Instruction instruction, DexType originHolder) {
     if (!instruction.isInvokeMethod()) {
       return;
     }
@@ -1376,9 +1377,8 @@
     }
     DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
     if (itemBasedString == null) {
-      if (options.proguardConfiguration.isObfuscating()) {
-        warnUndeterminedIdentifier(options.reporter, invokedMethod, origin, instruction, null);
-      }
+      warnUndeterminedIdentifierIfNecessary(
+          appInfo, options, invokedMethod, originHolder, instruction, null);
       return;
     }
     if (itemBasedString.basedOn instanceof DexType) {
diff --git a/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java b/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java
deleted file mode 100644
index 781d2b4..0000000
--- a/src/test/examples/uninitializedfinal/UninitializedFinalFieldLeak.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2018 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 uninitializedfinal;
-
-// Test that leaks an instance before its final field has been initialized to a thread that
-// reads that field. This tests that redundant field load elimination does not eliminate
-// field reads (even of final fields) that cross a monitor operation.
-public class UninitializedFinalFieldLeak {
-
-  public static class PollingThread extends Thread {
-    public int result = 0;
-    UninitializedFinalFieldLeak f;
-
-    PollingThread(UninitializedFinalFieldLeak f) {
-      this.f = f;
-    }
-
-    // Read the field a number of times. Then lock on the object to await field initialization.
-    public void run() {
-      result += f.i;
-      result += f.i;
-      result += f.i;
-      f.threadReadsDone = true;
-      synchronized (f) {
-        result += f.i;
-      }
-      // The right result is 42. Reading the uninitialized 0 three times and then
-      // reading the initialized value. It is safe to remove the two redundant loads
-      // before the monitor operation.
-      System.out.println(result);
-    }
-  }
-
-  public final int i;
-  public volatile boolean threadReadsDone = false;
-
-  public UninitializedFinalFieldLeak() throws InterruptedException {
-    // Leak the object to a thread and start the thread with the lock on the object taken.
-    // Then allow the other thread to run and read the uninitialized field.
-    // Finally, initialize the field and release the lock.
-    PollingThread t = new PollingThread(this);
-    synchronized (this) {
-      t.start();
-      while (!threadReadsDone) {
-        Thread.yield();
-      }
-      i = 42;
-    }
-    t.join();
-  }
-
-  public static void main(String[] args) throws InterruptedException {
-    new UninitializedFinalFieldLeak();
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 221d427..36f7594 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -45,11 +45,9 @@
         "instancevariable.InstanceVariable",
         "instanceofstring.InstanceofString",
         "invoke.Invoke",
-        "invokeempty.InvokeEmpty",
         "jumbostring.JumboString",
         "loadconst.LoadConst",
         "loop.UdpServer",
-        "nestedtrycatches.NestedTryCatches",
         "newarray.NewArray",
         "regalloc.RegAlloc",
         "returns.Returns",
@@ -60,7 +58,9 @@
         "throwing.Throwing",
         "trivial.Trivial",
         "trycatch.TryCatch",
+        "nestedtrycatches.NestedTryCatches",
         "trycatchmany.TryCatchMany",
+        "invokeempty.InvokeEmpty",
         "regress.Regress",
         "regress2.Regress2",
         "regress_37726195.Regress",
@@ -82,7 +82,6 @@
         "enclosingmethod_proguarded.Main",
         "interfaceinlining.Main",
         "switchmaps.Switches",
-        "uninitializedfinal.UninitializedFinalFieldLeak",
     };
 
     List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 5200a09..7b4f199 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -56,6 +56,8 @@
 
   // Actually running Proguard should only be during development.
   private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
+  // Actually running r8.jar in a forked process.
+  private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
 
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -71,6 +73,13 @@
   }
 
   /**
+   * Check if tests should run R8 in a forked process when applicable.
+   */
+  protected boolean isRunR8Jar() {
+    return RUN_R8_JAR;
+  }
+
+  /**
    * Write lines of text to a temporary file.
    */
   protected Path writeTextToTempFile(String... lines) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 10f62a1..200a1e7 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -70,6 +70,7 @@
 public class ToolHelper {
 
   public static final String BUILD_DIR = "build/";
+  public static final String LIBS_DIR = BUILD_DIR + "libs/";
   public static final String TESTS_DIR = "src/test/";
   public static final String EXAMPLES_DIR = TESTS_DIR + "examples/";
   public static final String EXAMPLES_ANDROID_O_DIR = TESTS_DIR + "examplesAndroidO/";
@@ -78,6 +79,8 @@
   public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
   public static final String EXAMPLES_BUILD_DIR = TESTS_BUILD_DIR + "examples/";
   public static final String EXAMPLES_KOTLIN_BUILD_DIR = TESTS_BUILD_DIR + "examplesKotlin/";
+  public static final String EXAMPLES_KOTLIN_RESOURCE_DIR =
+      TESTS_BUILD_DIR + "kotlinR8TestResources/";
   public static final String EXAMPLES_ANDROID_N_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidN/";
   public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
@@ -979,6 +982,12 @@
     return forkJava(dir, R8.class, args);
   }
 
+  public static ProcessResult forkR8Jar(Path dir, String... args)
+      throws IOException, InterruptedException {
+    String r8Jar = Paths.get(LIBS_DIR,  "r8.jar").toAbsolutePath().toString();
+    return forkJavaWithJar(dir, r8Jar, Arrays.asList(args));
+  }
+
   public static ProcessResult forkGenerateMainDexList(Path dir, List<String> args1, String... args2)
       throws IOException, InterruptedException {
     List<String> args = new ArrayList<>();
@@ -997,8 +1006,18 @@
     return forkJava(dir, clazz, Arrays.asList(args));
   }
 
+  private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
+      throws IOException {
+    List<String> command = new ImmutableList.Builder<String>()
+        .add(getJavaExecutable())
+        .add("-jar").add(jarPath)
+        .addAll(args)
+        .build();
+    return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+  }
+
   private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
-      throws IOException, InterruptedException {
+      throws IOException {
     List<String> command = new ImmutableList.Builder<String>()
         .add(getJavaExecutable())
         .add("-cp").add(System.getProperty("java.class.path"))
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index e1431a6..062095c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -43,7 +43,7 @@
     abstract boolean match(DexClass clazz);
   }
 
-  private static class Group extends LambdaOrGroup {
+  static class Group extends LambdaOrGroup {
     final String pkg;
     final String capture;
     final int arity;
@@ -83,31 +83,32 @@
     }
   }
 
-  private Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
+  private static Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
     assertEquals(capture.isEmpty(), singletons != 0);
     return new Group(pkg, capture, arity, KOTLIN_FUNCTION_IFACE_STR + arity, singletons);
   }
 
-  private Group kstyle(String pkg, int arity, int singletons) {
+  static Group kstyle(String pkg, int arity, int singletons) {
     assertTrue(singletons != 0);
     return kstyleImpl(pkg, "", arity, singletons);
   }
 
-  private Group kstyle(String pkg, String capture, int arity) {
+  private static Group kstyle(String pkg, String capture, int arity) {
     assertFalse(capture.isEmpty());
     return kstyleImpl(pkg, capture, arity, 0);
   }
 
-  private Group jstyleImpl(String pkg, String capture, int arity, String sam, int singletons) {
+  private static Group jstyleImpl(
+      String pkg, String capture, int arity, String sam, int singletons) {
     assertTrue(capture.isEmpty() || singletons == 0);
     return new Group(pkg, capture, arity, sam, singletons);
   }
 
-  private Group jstyle(String pkg, String capture, int arity, String sam) {
+  private static Group jstyle(String pkg, String capture, int arity, String sam) {
     return jstyleImpl(pkg, capture, arity, sam, 0);
   }
 
-  private Group jstyle(String pkg, int arity, String sam, int singletons) {
+  private static Group jstyle(String pkg, int arity, String sam, int singletons) {
     return jstyleImpl(pkg, "", arity, sam, singletons);
   }
 
@@ -116,7 +117,7 @@
     final String name;
     final int arity;
 
-    private Lambda(String pkg, String name, int arity) {
+    Lambda(String pkg, String name, int arity) {
       this.pkg = pkg;
       this.name = name;
       this.arity = arity;
@@ -143,7 +144,15 @@
     final List<DexClass> groups = new ArrayList<>();
 
     Verifier(AndroidApp app) throws IOException, ExecutionException {
-      this.dexInspector = new DexInspector(app);
+      this(new DexInspector(app));
+    }
+
+    Verifier(DexInspector dexInspector) {
+      this.dexInspector = dexInspector;
+      initGroupsAndLambdas();
+    }
+
+    private void initGroupsAndLambdas() {
       dexInspector.forAllClasses(clazz -> {
         DexClass dexClass = clazz.getDexClass();
         if (isLambdaOrGroup(dexClass)) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
new file mode 100644
index 0000000..2beb777
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, 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.kotlin;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_KOTLIN_RESOURCE_DIR;
+import static com.android.tools.r8.kotlin.KotlinLambdaMergingTest.kstyle;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Group;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Lambda;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Verifier;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+
+public class KotlinxMetadataExtensionsServiceTest extends TestBase {
+
+  private void forkR8_kstyle_trivial(boolean allowAccessModification) throws Exception {
+    if  (!isRunR8Jar()) {
+      return;
+    }
+    Path working = temp.getRoot().toPath();
+    Path kotlinJar =
+        Paths.get(EXAMPLES_KOTLIN_RESOURCE_DIR, "JAVA_8", "lambdas_kstyle_trivial.jar")
+            .toAbsolutePath();
+    Path output = working.resolve("classes.dex");
+    assertFalse(Files.exists(output));
+    Path proguardConfiguration = temp.newFile("test.conf").toPath();
+    List<String> lines = ImmutableList.of(
+        "-keepattributes Signature,InnerClasses,EnclosingMethod",
+        "-keep class **MainKt {",
+        "  public static void main(...);",
+        "}",
+        "-printmapping",
+        "-dontobfuscate",
+        allowAccessModification ? "-allowaccessmodification" : ""
+    );
+    FileUtils.writeTextFile(proguardConfiguration, lines);
+    ProcessResult result = ToolHelper.forkR8Jar(working,
+        "--pg-conf", proguardConfiguration.toString(),
+        "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.O).toAbsolutePath().toString(),
+        kotlinJar.toString());
+    assertEquals(0, result.exitCode);
+    assertThat(result.stderr, not(containsString(
+        "No MetadataExtensions instances found in the classpath")));
+    assertTrue(Files.exists(output));
+
+    DexInspector inspector = new DexInspector(output);
+    Verifier verifier = new Verifier(inspector);
+    String pkg = "lambdas_kstyle_trivial";
+    verifier.assertLambdaGroups(
+        allowAccessModification ?
+            new Group[]{
+                kstyle("", 0, 4),
+                kstyle("", 1, 8),
+                kstyle("", 2, 2), // -\
+                kstyle("", 2, 5), // - 3 groups different by main method
+                kstyle("", 2, 4), // -/
+                kstyle("", 3, 2),
+                kstyle("", 22, 2)} :
+            new Group[]{
+                kstyle(pkg, 0, 2),
+                kstyle(pkg, 1, 4),
+                kstyle(pkg, 2, 5), // - 2 groups different by main method
+                kstyle(pkg, 2, 4), // -/
+                kstyle(pkg, 3, 2),
+                kstyle(pkg, 22, 2),
+                kstyle(pkg + "/inner", 0, 2),
+                kstyle(pkg + "/inner", 1, 4)}
+    );
+
+    verifier.assertLambdas(
+        allowAccessModification ?
+            new Lambda[]{
+                new Lambda(pkg, "MainKt$testStateless$6", 1) /* Banned for limited inlining */} :
+            new Lambda[]{
+                new Lambda(pkg, "MainKt$testStateless$6", 1), /* Banned for limited inlining */
+                new Lambda(pkg, "MainKt$testStateless$8", 2),
+                new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)}
+    );
+  }
+
+  @Test
+  public void testTrivialKs_allowAccessModification() throws Exception {
+    forkR8_kstyle_trivial(true);
+  }
+
+  @Test
+  public void testTrivialKs_notAllowAccessModification() throws Exception {
+    forkR8_kstyle_trivial(false);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index 9a9ca34..ec27895 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -21,8 +21,8 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import java.util.List;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -46,7 +46,7 @@
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  *;",
@@ -97,7 +97,6 @@
     doTest_keepAll(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepAll_R8Compat() throws Exception {
@@ -137,7 +136,7 @@
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  !public <methods>;",
@@ -188,7 +187,6 @@
     doTest_keepNonPublic(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepNonPublic_R8Compat() throws Exception {
@@ -222,13 +220,13 @@
       boolean minify) throws Exception {
     Class mainClass = TestMain.class;
     String keep = !minify ? "-keep" : "-keep,allowobfuscation";
-    List<String> config = ImmutableList.of(
+    Iterable<String> config = ImmutableList.of(
         "-printmapping",
         repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  public <methods>;",
@@ -238,6 +236,13 @@
         "}",
         "-dontwarn java.lang.invoke.*"
     );
+    if (isR8(shrinker)) {
+      config = Iterables.concat(config, ImmutableList.of(
+          "-neverinline class " + TestClass.class.getCanonicalName() + " {",
+          "  * staticMethod();",
+          "}"
+      ));
+    }
 
     AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
     assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
@@ -253,15 +258,11 @@
 
     // Test an indirectly referred method.
     staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
-    if (isR8(shrinker)) {
-      // Inlined.
-      assertThat(staticMethod, not(isPresent()));
-    } else {
-      assertThat(staticMethod, isPresent());
-      assertEquals(minify, staticMethod.isRenamed());
-      boolean publicizeCondition = minify && repackagePrefix != null && allowAccessModification;
-      assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
-    }
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+        : minify && repackagePrefix != null && allowAccessModification;
+    assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
   }
 
   @Test
@@ -277,7 +278,6 @@
     doTest_keepPublic(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepPublic_R8Compat() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 693f02d..e3bffc6 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -44,7 +44,7 @@
   }
 
   protected AndroidApp runShrinkerRaw(
-      Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
+      Shrinker mode, List<Class> programClasses, Iterable<String> proguadConfigs) throws Exception {
     return runShrinkerRaw(
         mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
   }
@@ -111,10 +111,11 @@
   protected AndroidApp runR8CompatRaw(
       List<Class> programClasses, String proguardConfig) throws Exception {
     CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
+    ToolHelper.allowTestProguardOptions(builder);
     builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
     programClasses.forEach(
         clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
-    builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
+    builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
     builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
     return ToolHelper.runR8(builder.build());
   }