Merge "Replace ZipInputstream by ZipFile"
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 52fff08..1cc21a3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -124,6 +124,12 @@
debugValues.clear();
}
+ public void moveDebugValue(Value value, Instruction target) {
+ assert debugValues.contains(value);
+ value.replaceDebugUser(this, target);
+ debugValues.remove(value);
+ }
+
/**
* Returns the basic block containing this instruction.
*/
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 5eeda96..32159c1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -79,6 +79,7 @@
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -1789,6 +1790,26 @@
assert returnType == MoveType.fromDexType(method.method.proto.returnType);
}
closeCurrentBlock();
+
+ // Collect the debug values which are live on all returns.
+ Set<Value> debugValuesForReturn = Sets.newIdentityHashSet();
+ for (Value value : exitBlocks.get(0).exit().getDebugValues()) {
+ boolean include = true;
+ for (int i = 1; i < exitBlocks.size() && include; i++) {
+ include = exitBlocks.get(i).exit().getDebugValues().contains(value);
+ }
+ if (include) {
+ debugValuesForReturn.add(value);
+ }
+ }
+
+ // Move all these debug values to the new return.
+ for (Value value : debugValuesForReturn) {
+ for (BasicBlock block : exitBlocks) {
+ block.exit().moveDebugValue(value, normalExitBlock.exit());
+ }
+ }
+
// Replace each return instruction with a goto to the new exit block.
List<Value> operands = new ArrayList<>();
for (BasicBlock block : exitBlocks) {
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 ea1ef57..24e0396 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
@@ -569,7 +569,6 @@
LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code, options);
registerAllocator.allocateRegisters(options.debug);
printMethod(code, "After register allocation (non-SSA)");
- printLiveRanges(registerAllocator, "Final live ranges.");
if (!options.debug) {
CodeRewriter.removedUnneededDebugPositions(code);
}
@@ -604,10 +603,4 @@
printer.end("cfg");
}
}
-
- private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) {
- if (printer != null) {
- allocator.print(printer, title);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index f4c51ce..9b3eb83 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -2216,25 +2216,6 @@
return true;
}
- public void print(CfgPrinter printer, String title) {
- printer.begin("intervals");
- printer.print("name \"").append(title).append("\"").ln();
- PriorityQueue<LiveIntervals> sortedIntervals =
- new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
- sortedIntervals.addAll(liveIntervals);
- for (LiveIntervals interval = sortedIntervals.poll();
- interval != null;
- interval = sortedIntervals.poll()) {
- Value value = interval.getValue();
- if (interval.getRanges().get(0).isInfinite()) {
- // Skip argument sentinels.
- continue;
- }
- interval.print(printer, value.getNumber(), value.getNumber());
- }
- printer.end("intervals");
- }
-
@Override
public String toString() {
StringBuilder builder = new StringBuilder("Live ranges:\n");
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
index d42dd61..890cb85 100644
--- a/src/test/debugTestResources/Locals.java
+++ b/src/test/debugTestResources/Locals.java
@@ -244,6 +244,24 @@
return sum + x + y;
}
+ public static int argumentLiveAtReturn(int x) {
+ switch (x) {
+ case 0:
+ return 0;
+ case 1:
+ return 0;
+ case 2:
+ return 0;
+ case 100:
+ return 1;
+ case 101:
+ return 1;
+ case 102:
+ return 1;
+ }
+ return -1;
+ }
+
public static void main(String[] args) {
noLocals();
unusedLocals();
@@ -259,5 +277,6 @@
stepNonEmptyForLoopBody(3);
tempInCase(42);
localSwap(1, 2);
+ argumentLiveAtReturn(-1);
}
}
diff --git a/src/test/debugTestResourcesKotlin/KotlinApp.kt b/src/test/debugTestResourcesKotlin/KotlinApp.kt
index 7fa2be1..094407b 100644
--- a/src/test/debugTestResourcesKotlin/KotlinApp.kt
+++ b/src/test/debugTestResourcesKotlin/KotlinApp.kt
@@ -3,42 +3,27 @@
// BSD-style license that can be found in the LICENSE file.
class KotlinApp {
+
+ fun ifElse(cond: Boolean) {
+ val a = 10
+ if (cond) {
+ val b = a * 2
+ printInt(b)
+ } else {
+ val c = a / 2
+ print(c)
+ }
+ }
+
+ fun printInt(i: Int) {
+ println(i)
+ }
+
companion object {
@JvmStatic fun main(args: Array<String>) {
- println("Hello world!")
val instance = KotlinApp()
- instance.processObject(instance, instance::printObject)
- instance.invokeInlinedFunctions()
+ instance.ifElse(true)
+ instance.ifElse(false)
}
}
-
- fun processObject(obj: Any, func: (Any) -> Unit) {
- func(obj)
- }
-
- 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/debugTestResourcesKotlin/KotlinInline.kt b/src/test/debugTestResourcesKotlin/KotlinInline.kt
new file mode 100644
index 0000000..7f914e4
--- /dev/null
+++ b/src/test/debugTestResourcesKotlin/KotlinInline.kt
@@ -0,0 +1,59 @@
+// Copyright (c) 2017, 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.
+
+class KotlinInline {
+
+ fun processObject(obj: Any, func: (Any) -> Unit) {
+ func(obj)
+ }
+
+ 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")
+ }
+
+ fun emptyMethod(unused: Int) {
+ }
+
+ fun singleInline() {
+ emptyMethod(0)
+ inlined()
+ emptyMethod(1)
+ }
+
+ inline fun inlined() {
+ emptyMethod(-1)
+ }
+
+ companion object {
+ @JvmStatic fun main(args: Array<String>) {
+ println("Hello world!")
+ val instance = KotlinInline()
+ instance.processObject(instance, instance::printObject)
+ instance.invokeInlinedFunctions()
+ instance.singleInline()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 6340728..b2b77bc 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -973,7 +973,7 @@
.filter(m -> methodSignature.equals(m.methodSignature)).collect(
Collectors.toList());
}
- Assert.assertFalse("No method found", methodInfos.isEmpty());
+ Assert.assertFalse("No method named " + methodName + " found", methodInfos.isEmpty());
// There must be only one matching method
Assert.assertEquals("More than 1 method found: please specify a signature", 1,
methodInfos.size());
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
new file mode 100644
index 0000000..e62a17b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2017, 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.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class KotlinInlineTest extends DebugTestBase {
+
+ @Ignore("Requires kotlin-specific stepping behavior")
+ @Test
+ public void testStepOverInline() throws Throwable {
+ String methodName = "singleInline";
+ runDebugTestKotlin("KotlinInline",
+ breakpoint("KotlinInline", methodName),
+ run(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(41, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ // TODO(shertz) stepping over must take kotlin inline range into account.
+ stepOver(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(42, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ stepOver(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(43, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ run());
+ }
+
+ @Ignore("Requires kotlin-specific stepping behavior")
+ @Test
+ public void testStepIntoInline() throws Throwable {
+ String methodName = "singleInline";
+ runDebugTestKotlin("KotlinInline",
+ breakpoint("KotlinInline", methodName),
+ run(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(41, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ // TODO(shertz) stepping over must take kotlin inline range into account.
+ stepInto(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ // The actual line number (the one encoded in debug information) is different than the
+ // source file one.
+ // TODO(shertz) extract original line number from JSR-45's SMAP (only supported on
+ // Android O+).
+ assertTrue(42 != s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ run());
+ }
+
+ @Ignore("Requires kotlin-specific stepping behavior")
+ @Test
+ public void testStepOutInline() throws Throwable {
+ String methodName = "singleInline";
+ runDebugTestKotlin("KotlinInline",
+ breakpoint("KotlinInline", methodName),
+ run(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(41, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ // TODO(shertz) stepping out must take kotlin inline range into account.
+ stepInto(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ }),
+ stepOut(),
+ inspect(s -> {
+ assertEquals("KotlinInline", s.getClassName());
+ assertEquals(methodName, s.getMethodName());
+ assertEquals("KotlinInline.kt", s.getSourceFile());
+ assertEquals(43, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ run());
+ }
+
+ @Test
+ public void testKotlinInline() throws Throwable {
+ final String inliningMethodName = "invokeInlinedFunctions";
+ runDebugTestKotlin("KotlinInline",
+ breakpoint("KotlinInline", inliningMethodName),
+ run(),
+ inspect(s -> {
+ assertEquals(inliningMethodName, s.getMethodName());
+ assertEquals(16, 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.
+ 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 -> {
+ assertEquals(inliningMethodName, s.getMethodName());
+ assertEquals(17, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ stepInto(),
+ inspect(s -> {
+ assertEquals(inliningMethodName, s.getMethodName());
+ assertEquals(18, 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.
+ 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 -> {
+ assertEquals(inliningMethodName, s.getMethodName());
+ assertEquals(19, s.getLineNumber());
+ s.checkLocal("this");
+ }),
+ stepInto(),
+ inspect(s -> {
+ assertEquals(inliningMethodName, s.getMethodName());
+ assertEquals(20, 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());
+ }
+
+}
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 8caa3c1..a6b57b8 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -3,97 +3,160 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import static org.junit.Assert.assertEquals;
+
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.Assert;
import org.junit.Test;
public class KotlinTest extends DebugTestBase {
+ // TODO(shertz) simplify test
+ // TODO(shertz) add more variables ?
@Test
- public void testKotlinApp() throws Throwable {
- final String inliningMethodName = "invokeInlinedFunctions";
+ public void testStepOver() throws Throwable {
runDebugTestKotlin("KotlinApp",
breakpoint("KotlinApp$Companion", "main"),
run(),
inspect(s -> {
- Assert.assertEquals("KotlinApp.kt", s.getSourceFile());
- Assert.assertEquals(8, s.getLineNumber());
+ assertEquals("KotlinApp$Companion", s.getClassName());
+ assertEquals("KotlinApp.kt", s.getSourceFile());
+ assertEquals(24, s.getLineNumber());
s.checkLocal("this");
s.checkLocal("args");
+ checkNoLocal("instance");
}),
stepOver(),
inspect(s -> {
- Assert.assertEquals(9, s.getLineNumber());
- s.checkLocal("this");
- s.checkLocal("args");
- }),
- stepOver(),
- inspect(s -> {
- Assert.assertEquals(10, s.getLineNumber());
+ assertEquals(25, s.getLineNumber());
s.checkLocal("this");
s.checkLocal("args");
s.checkLocal("instance");
}),
stepOver(),
inspect(s -> {
- Assert.assertEquals(11, s.getLineNumber());
+ assertEquals(26, s.getLineNumber());
s.checkLocal("this");
s.checkLocal("args");
s.checkLocal("instance");
}),
+ run());
+ }
+
+ @Test
+ public void testStepIntoAndOut() throws Throwable {
+ runDebugTestKotlin("KotlinApp",
+ breakpoint("KotlinApp$Companion", "main"),
+ run(),
+ inspect(s -> {
+ assertEquals("KotlinApp$Companion", s.getClassName());
+ assertEquals("KotlinApp.kt", s.getSourceFile());
+ assertEquals(24, s.getLineNumber());
+ s.checkLocal("this");
+ s.checkLocal("args");
+ checkNoLocal("instance");
+ }),
+ stepOver(),
+ inspect(s -> {
+ assertEquals(25, s.getLineNumber());
+ s.checkLocal("this");
+ s.checkLocal("args");
+ s.checkLocal("instance");
+ }),
+ // Step into 1st invoke of ifElse
stepInto(),
inspect(s -> {
- Assert.assertEquals(inliningMethodName, s.getMethodName());
- Assert.assertEquals(24, s.getLineNumber());
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals("KotlinApp.kt", s.getSourceFile());
+ assertEquals(8, s.getLineNumber());
s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(true));
+ checkNoLocal("a");
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
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.
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(9, s.getLineNumber());
s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(true));
+ s.checkLocal("a", Value.createInt(10));
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
stepInto(),
inspect(s -> {
- Assert.assertEquals(inliningMethodName, s.getMethodName());
- Assert.assertEquals(25, s.getLineNumber());
+ // We should be into the 'then' statement.
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(10, s.getLineNumber());
s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(true));
+ s.checkLocal("a", Value.createInt(10));
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
stepInto(),
inspect(s -> {
- Assert.assertEquals(inliningMethodName, s.getMethodName());
- Assert.assertEquals(26, s.getLineNumber());
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(11, 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");
+ s.checkLocal("cond", Value.createBoolean(true));
+ s.checkLocal("a", Value.createInt(10));
+ s.checkLocal("b", Value.createInt(20));
+ checkNoLocal("c");
+ }),
+ // Go back to the main method
+ stepOut(),
+ inspect(s -> {
+ assertEquals("KotlinApp$Companion", s.getClassName());
+ assertEquals("KotlinApp.kt", s.getSourceFile());
+ assertEquals(26, s.getLineNumber());
+ s.checkLocal("this");
+ s.checkLocal("args");
+ checkNoLocal("instance");
+ }),
+ // Step into 2nd invoke of ifElse
+ stepInto(),
+ inspect(s -> {
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals("KotlinApp.kt", s.getSourceFile());
+ assertEquals(8, s.getLineNumber());
+ s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(false));
+ checkNoLocal("a");
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
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.
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(9, s.getLineNumber());
s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(false));
+ s.checkLocal("a", Value.createInt(10));
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
stepInto(),
inspect(s -> {
- Assert.assertEquals(inliningMethodName, s.getMethodName());
- Assert.assertEquals(27, s.getLineNumber());
+ // We should be into the 'else' statement this time.
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(13, s.getLineNumber());
s.checkLocal("this");
+ s.checkLocal("cond", Value.createBoolean(false));
+ s.checkLocal("a", Value.createInt(10));
+ checkNoLocal("b");
+ checkNoLocal("c");
}),
stepInto(),
inspect(s -> {
- Assert.assertEquals(inliningMethodName, s.getMethodName());
- Assert.assertEquals(28, s.getLineNumber());
+ assertEquals("KotlinApp", s.getClassName());
+ assertEquals(14, 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");
+ s.checkLocal("cond", Value.createBoolean(false));
+ s.checkLocal("a", Value.createInt(10));
+ checkNoLocal("b");
+ s.checkLocal("c", Value.createInt(5));
}),
run());
}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 02fb694..6f73f02c 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -470,4 +470,18 @@
checkNoLocal("t"),
run());
}
+
+ @Test
+ public void argumentLiveAtReturn() throws Throwable {
+ runDebugTest(
+ "Locals",
+ breakpoint("Locals", "argumentLiveAtReturn"),
+ run(),
+ checkLine(SOURCE_FILE, 248),
+ stepOver(),
+ checkLine(SOURCE_FILE, 262),
+ checkLocal("x", Value.createInt(-1)),
+ checkNoLocal("t"),
+ run());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index ad6f925..31872ed 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
@@ -102,7 +103,8 @@
DexDebugEntry previousEntry = null;
for (DexDebugEntry entry : entries) {
if (previousEntry != null) {
- assertTrue("More than one entry defined for PC " + entry.address,
+ assertTrue(
+ "More than one entry defined for PC " + StringUtils.hexString(entry.address, 2),
entry.address > previousEntry.address);
}
previousEntry = entry;
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 2782c91..5ffd815 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -336,4 +336,105 @@
info.checkLineHasExactLocals(8, "param", "int", "x", "int");
info.checkLineHasExactLocals(9, "param", "int");
}
+
+ @Test
+ public void argumentLiveAtReturn() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ /*
+ This is the original Java source code.
+
+ public static int argumentLiveAtReturn(int x) { // Line 1
+ switch (x) {
+ case 0:
+ return 0;
+ case 1:
+ return 0;
+ case 2:
+ return 0;
+ case 100:
+ return 1;
+ case 101:
+ return 1;
+ case 102:
+ return 1;
+ }
+ return -1;
+ }
+ */
+ MethodSignature foo = clazz.addStaticMethod("argumentLiveAtReturn", ImmutableList.of("I"), "I",
+ ".limit stack 2",
+ ".limit locals 1",
+ ".var 0 is x I from L0 to L8",
+ "L0:",
+ ".line 2",
+ " iload 0",
+ "lookupswitch",
+ " 0: L1",
+ " 1: L2",
+ " 2: L3",
+ " 100: L4",
+ " 101: L5",
+ " 102: L6",
+ " default: L7",
+ "L1:",
+ ".line 4",
+ " iconst_0",
+ " ireturn",
+ "L2:",
+ ".line 6",
+ " iconst_0",
+ " ireturn",
+ "L3:",
+ ".line 8",
+ " iconst_0",
+ " ireturn",
+ "L4:",
+ ".line 10",
+ " iconst_1",
+ " ireturn",
+ "L5:",
+ ".line 12",
+ " iconst_1",
+ " ireturn",
+ "L6:",
+ ".line 14",
+ " iconst_1",
+ " ireturn",
+ "L7:",
+ ".line 16",
+ " iconst_m1",
+ " ireturn",
+ "L8:"
+ );
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " ldc -1",
+ " invokestatic Test/argumentLiveAtReturn(I)I",
+ " invokevirtual java/io/PrintStream/print(I)V",
+ " return");
+
+ String expected = "-1";
+ String javaResult = runOnJava(builder, clazz.name);
+ assertEquals(expected, javaResult);
+
+ AndroidApp jasminApp = builder.build();
+ AndroidApp d8App = ToolHelper.runD8(jasminApp);
+ String artResult = runOnArt(d8App, clazz.name);
+ assertEquals(expected, artResult);
+ DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+ info.checkStartLine(2);
+ info.checkLineHasExactLocals(2, "x", "int");
+ info.checkLineHasExactLocals(4, "x", "int");
+ info.checkLineHasExactLocals(6, "x", "int");
+ info.checkLineHasExactLocals(8, "x", "int");
+ info.checkLineHasExactLocals(10, "x", "int");
+ info.checkLineHasExactLocals(12, "x", "int");
+ info.checkLineHasExactLocals(14, "x", "int");
+ info.checkLineHasExactLocals(16, "x", "int");
+ }
}
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 9e5f63f..d511bcf 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -57,16 +57,22 @@
' peak resident set size (VmHWM) in bytes.',
default = False,
action = 'store_true')
+ parser.add_argument('--output',
+ help = 'Output directory to keep the generated files')
return parser.parse_args()
def Main():
utils.check_java_version()
args = parse_arguments()
+ output_dir = args.output
with utils.TempDir() as temp_dir:
+ if not output_dir:
+ output_dir = temp_dir
+
if args.tool in ['dx', 'goyt', 'goyt-release']:
- tool_args = ['--dex', '--output=' + temp_dir, '--multi-dex',
+ tool_args = ['--dex', '--output=' + output_dir, '--multi-dex',
'--min-sdk-version=' + MIN_SDK_VERSION]
xmx = None
@@ -80,7 +86,7 @@
xmx = '-Xmx1600m'
else:
tool_file = D8_JAR
- tool_args = ['--output', temp_dir, '--min-api', MIN_SDK_VERSION]
+ tool_args = ['--output', output_dir, '--min-api', MIN_SDK_VERSION]
if args.tool == 'd8-release':
tool_args.append('--release')
xmx = '-Xmx600m'
@@ -89,7 +95,7 @@
track_memory_file = None
if args.print_memoryuse:
- track_memory_file = os.path.join(temp_dir, utils.MEMORY_USE_TMP_FILE)
+ track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE)
cmd.extend(['tools/track_memory.sh', track_memory_file])
if tool_file.endswith('.jar'):
@@ -108,7 +114,7 @@
print('{}-Total(MemoryUse): {}'
.format(args.name, utils.grep_memoryuse(track_memory_file)))
- dex_files = [f for f in glob(os.path.join(temp_dir, '*.dex'))]
+ dex_files = [f for f in glob(os.path.join(output_dir, '*.dex'))]
code_size = 0
for dex_file in dex_files:
code_size += os.path.getsize(dex_file)