Merge "Fix CompatDX's ignoreDexInArchieve"
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 8d36ef7..ca1d53c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -31,6 +31,8 @@
 import com.android.tools.r8.ir.conversion.JarState.Local;
 import com.android.tools.r8.ir.conversion.JarState.Slot;
 import com.android.tools.r8.logging.Log;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -257,9 +259,39 @@
 
   @Override
   public void buildPrelude(IRBuilder builder) {
-    Map<Integer, MoveType> initializedLocals = new HashMap<>(node.localVariables.size());
     // Record types for arguments.
-    recordArgumentTypes(initializedLocals);
+    Map<Integer, MoveType> initializedLocals = recordArgumentTypes();
+    // Initialize all non-argument locals to ensure safe insertion of debug-local instructions.
+    for (Object o : node.localVariables) {
+      LocalVariableNode local = (LocalVariableNode) o;
+      Type localType = Type.getType(local.desc);
+      int sort = localType.getSort();
+      switch (sort) {
+        case Type.OBJECT:
+        case Type.ARRAY:
+          localType = JarState.NULL_TYPE;
+          break;
+        case Type.DOUBLE:
+        case Type.LONG:
+        case Type.FLOAT:
+          break;
+        case Type.VOID:
+        case Type.METHOD:
+          throw new Unreachable("Invalid local variable type: " + localType);
+        default:
+          localType = Type.INT_TYPE;
+          break;
+      }
+      int localRegister = state.getLocalRegister(local.index, localType);
+      MoveType existingLocalType = initializedLocals.get(localRegister);
+      assert existingLocalType == null || existingLocalType == moveType(localType);
+      if (existingLocalType == null) {
+        int localRegister2 = state.writeLocal(local.index, localType);
+        assert localRegister == localRegister2;
+        initializedLocals.put(localRegister, moveType(localType));
+        builder.addDebugUninitialized(localRegister, constType(localType));
+      }
+    }
     // Add debug information for all locals at the initial label.
     if (initialLabel != null) {
       state.openLocals(initialLabel);
@@ -285,37 +317,6 @@
       monitorEnter = builder.addMonitor(Monitor.Type.ENTER, monitorRegister);
       generatingMethodSynchronization = false;
     }
-    // Initialize all non-argument locals to ensure safe insertion of debug-local instructions.
-    for (Object o : node.localVariables) {
-      LocalVariableNode local = (LocalVariableNode) o;
-      Type localType = Type.getType(local.desc);
-      int sort = localType.getSort();
-      switch (sort) {
-        case Type.OBJECT:
-        case Type.ARRAY:
-          localType = JarState.NULL_TYPE;
-          break;
-        case Type.DOUBLE:
-        case Type.LONG:
-        case Type.FLOAT:
-          break;
-        case Type.VOID:
-        case Type.METHOD:
-          throw new Unreachable("Invalid local variable type: " + localType);
-        default:
-          localType = Type.INT_TYPE;
-          break;
-      }
-      int localRegister = state.getLocalRegister(local.index, localType);
-      MoveType exitingLocalType = initializedLocals.get(localRegister);
-      assert exitingLocalType == null || exitingLocalType == moveType(localType);
-      if (exitingLocalType == null) {
-        int localRegister2 = state.writeLocal(local.index, localType);
-        assert localRegister == localRegister2;
-        initializedLocals.put(localRegister, moveType(localType));
-        builder.addDebugUninitialized(localRegister, constType(localType));
-      }
-    }
     computeBlockEntryJarStates(builder);
     state.setBuilding();
   }
@@ -335,7 +336,9 @@
     }
   }
 
-  private void recordArgumentTypes(Map<Integer, MoveType> initializedLocals) {
+  private Int2ReferenceMap<MoveType> recordArgumentTypes() {
+    Int2ReferenceMap<MoveType> initializedLocals =
+        new Int2ReferenceOpenHashMap<>(node.localVariables.size());
     int argumentRegister = 0;
     if (!isStatic()) {
       Type thisType = Type.getType(clazz.descriptor.toString());
@@ -348,6 +351,7 @@
       argumentRegister += moveType.requiredRegisters();
       initializedLocals.put(register, moveType);
     }
+    return initializedLocals;
   }
 
   private void computeBlockEntryJarStates(IRBuilder builder) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 2a3e431..03c614a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -344,14 +344,7 @@
 
   private Local setLocalInfoForRegister(int register, DebugLocalInfo info) {
     Local existingLocal = getLocalForRegister(register);
-    // TODO(ager, zerny): Kotlin debug information contains locals that are not referenced.
-    // That seems broken and we currently do not retain that debug information because
-    // we do not let locals debug information influence code generation. Debug information can
-    // be completely malformed, so we shouldn't let it influence code generation. However, we
-    // need to deal with these unused locals in the debug information. For now we
-    // use a null type for the slot, but we should reconsider that.
-    Slot slot = existingLocal != null ? existingLocal.slot : new Slot(register, null);
-    Local local = new Local(slot, info);
+    Local local = new Local(existingLocal.slot, info);
     locals[register] = local;
     return local;
   }
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 78d8369..815efe4 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
@@ -632,6 +632,7 @@
       InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
       if ((invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName
           || invoke.getInvokedMethod() == dexItemFactory.classMethods.getName)
+          && !invoke.inValues().get(0).isPhi()
           && invoke.inValues().get(0).definition.isConstClass()
           && invoke.inValues().get(0).definition.asConstClass().getValue()
           == method.method.getHolder()) {
diff --git a/src/test/debugTestResourcesKotlin/KotlinApp.kt b/src/test/debugTestResourcesKotlin/KotlinApp.kt
index 7c15337..7fa2be1 100644
--- a/src/test/debugTestResourcesKotlin/KotlinApp.kt
+++ b/src/test/debugTestResourcesKotlin/KotlinApp.kt
@@ -8,6 +8,7 @@
             println("Hello world!")
             val instance = KotlinApp()
             instance.processObject(instance, instance::printObject)
+            instance.invokeInlinedFunctions()
         }
     }
 
@@ -18,4 +19,26 @@
     fun printObject(obj: Any) {
         println(obj)
     }
+
+    fun invokeInlinedFunctions() {
+        inlinedA {
+            val inA = 1
+            inlinedB {
+                val inB = 2
+                foo(inA, inB)
+            }
+        }
+    }
+
+    inline fun inlinedA(f: () -> Unit) {
+        f()
+    }
+
+    inline fun inlinedB(f: () -> Unit) {
+        f()
+    }
+
+    fun foo(a: Int, b: Int) {
+        println("a=$a, b=$b")
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index 1700964..8caa3c1 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -10,6 +11,7 @@
 
   @Test
   public void testKotlinApp() throws Throwable {
+    final String inliningMethodName = "invokeInlinedFunctions";
     runDebugTestKotlin("KotlinApp",
         breakpoint("KotlinApp$Companion", "main"),
         run(),
@@ -32,6 +34,67 @@
           s.checkLocal("args");
           s.checkLocal("instance");
         }),
+        stepOver(),
+        inspect(s -> {
+          Assert.assertEquals(11, s.getLineNumber());
+          s.checkLocal("this");
+          s.checkLocal("args");
+          s.checkLocal("instance");
+        }),
+        stepInto(),
+        inspect(s -> {
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          Assert.assertEquals(24, s.getLineNumber());
+          s.checkLocal("this");
+        }),
+        stepInto(),
+        inspect(s -> {
+          // We must have stepped into the code of the inlined method but the actual current method
+          // did not change.
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          // TODO(shertz) get the original line if JSR45 is supported by the targeted ART runtime.
+          s.checkLocal("this");
+        }),
+        stepInto(),
+        inspect(s -> {
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          Assert.assertEquals(25, s.getLineNumber());
+          s.checkLocal("this");
+        }),
+        stepInto(),
+        inspect(s -> {
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          Assert.assertEquals(26, s.getLineNumber());
+          s.checkLocal("this");
+          s.checkLocal("inA", Value.createInt(1));
+          // This is a "hidden" lv added by Kotlin (which is neither initialized nor used).
+          s.checkLocal("$i$f$inlinedA");
+          s.checkLocal("$i$a$1$inlinedA");
+        }),
+        stepInto(),
+        inspect(s -> {
+          // We must have stepped into the code of the second inlined method but the actual current
+          // method did not change.
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          // TODO(shertz) get the original line if JSR45 is supported by the targeted ART runtime.
+          s.checkLocal("this");
+        }),
+        stepInto(),
+        inspect(s -> {
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          Assert.assertEquals(27, s.getLineNumber());
+          s.checkLocal("this");
+        }),
+        stepInto(),
+        inspect(s -> {
+          Assert.assertEquals(inliningMethodName, s.getMethodName());
+          Assert.assertEquals(28, s.getLineNumber());
+          s.checkLocal("this");
+          s.checkLocal("inB", Value.createInt(2));
+          // This is a "hidden" lv added by Kotlin (which is neither initialized nor used).
+          s.checkLocal("$i$f$inlinedB");
+          s.checkLocal("$i$a$1$inlinedB");
+        }),
         run());
   }