Merge "Do not warn undetermined identifiers for reflection in library classes."
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/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/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());
   }