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());
}