Merge "Fix the workaround for overlapping long registers art bug."
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index cb9fb20..dd29e8d2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -291,20 +291,24 @@
     return canonicalize(fields, field);
   }
 
-  public DexProto createProto(DexString shorty, DexType type, DexTypeList parameters) {
+  public DexField createField(DexType clazz, DexType type, String name) {
+    return createField(clazz, type, createString(name));
+  }
+
+  public DexProto createProto(DexString shorty, DexType returnType, DexTypeList parameters) {
     assert !sorted;
-    DexProto proto = new DexProto(shorty, type, parameters);
+    DexProto proto = new DexProto(shorty, returnType, parameters);
     return canonicalize(protos, proto);
   }
 
-  public DexProto createProto(DexString shorty, DexType type, DexType[] parameters) {
+  public DexProto createProto(DexString shorty, DexType returnType, DexType[] parameters) {
     assert !sorted;
-    return createProto(shorty, type,
+    return createProto(shorty, returnType,
         parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
   }
 
-  public DexProto createProto(DexType type, DexType[] parameters) {
-    return createProto(createShorty(type, parameters), type, parameters);
+  public DexProto createProto(DexType returnType, DexType[] parameters) {
+    return createProto(createShorty(returnType, parameters), returnType, parameters);
   }
 
   public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
@@ -322,6 +326,10 @@
     return canonicalize(methods, method);
   }
 
+  public DexMethod createMethod(DexType holder, DexProto proto, String name) {
+    return createMethod(holder, proto, createString(name));
+  }
+
   public DexMethodHandle createMethodHandle(
       MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
     assert !sorted;
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 232e0ff..8bb31d2 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
@@ -884,6 +884,21 @@
     return block;
   }
 
+  /**
+   * Create a new basic block with a single if instruction.
+   *
+   * <p>The constructed basic block has no predecessors and no successors.
+   *
+   * @param blockNumber the block number of the goto block
+   */
+  public static BasicBlock createIfBlock(int blockNumber, If theIf) {
+    BasicBlock block = new BasicBlock();
+    block.add(theIf);
+    block.close(null);
+    block.setNumber(blockNumber);
+    return block;
+  }
+
   public boolean isTrivialGoto() {
     return instructions.size() == 1 && exit().isGoto();
   }
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 4aa06d6..8370eb8 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
@@ -472,6 +472,11 @@
     codeRewriter.shortenLiveRanges(code);
     codeRewriter.identifyReturnsArgument(method, code);
 
+    // Insert code to log arguments if requested.
+    if (options.methodMatchesLogArgumentsFilter(method)) {
+      codeRewriter.logArgumentTypes(method, code);
+    }
+
     printMethod(code, "Optimized IR (SSA)");
     // Perform register allocation.
     RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
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 0830209..e5b8306 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
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.ArrayGet;
 import com.android.tools.r8.ir.code.ArrayPut;
@@ -21,6 +22,7 @@
 import com.android.tools.r8.ir.code.Cmp;
 import com.android.tools.r8.ir.code.Cmp.Bias;
 import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.ConstType;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.Goto;
@@ -34,6 +36,7 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeVirtual;
 import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.MoveType;
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
@@ -1277,4 +1280,114 @@
     }
     return false;
   }
+
+  private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
+    Value value = code.createValue(MoveType.OBJECT);;
+    iterator.add(new ConstString(value, dexItemFactory.createString(s)));
+    return value;
+  }
+
+  /**
+   * Insert code into <code>method</code> to log the argument types to System.out.
+   *
+   * The type is determined by calling getClass() on the argument.
+   */
+  public void logArgumentTypes(DexEncodedMethod method, IRCode code) {
+    List<Value> arguments = code.collectArguments();
+    BasicBlock block = code.blocks.getFirst();
+    InstructionListIterator iterator = block.listIterator();
+
+    // Split arguments into their own block.
+    iterator.nextUntil(instruction -> !instruction.isArgument());
+    iterator.previous();
+    iterator.split(code);
+    iterator.previous();
+
+    // Now that the block is split there should not be any catch handlers in the block.
+    assert !block.hasCatchHandlers();
+    Value out = code.createValue(MoveType.OBJECT);
+    DexType javaLangSystemType = dexItemFactory.createType("Ljava/lang/System;");
+    DexType javaIoPrintStreamType = dexItemFactory.createType("Ljava/io/PrintStream;");
+
+    DexProto proto = dexItemFactory.createProto(
+        dexItemFactory.voidType, new DexType[]{dexItemFactory.objectType});
+    DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
+    DexMethod printLn = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "println");
+
+    iterator.add(
+        new StaticGet(MemberType.OBJECT, out,
+            dexItemFactory.createField(javaLangSystemType, javaIoPrintStreamType, "out")));
+
+    Value value = code.createValue(MoveType.OBJECT);
+    iterator.add(new ConstString(value, dexItemFactory.createString("INVOKE ")));
+    iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+
+    value = code.createValue(MoveType.OBJECT);;
+    iterator.add(
+        new ConstString(value, dexItemFactory.createString(method.method.qualifiedName())));
+    iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+
+    Value openParenthesis = addConstString(code, iterator, "(");
+    Value comma = addConstString(code, iterator, ",");
+    Value closeParenthesis = addConstString(code, iterator, ")");
+    Value indent = addConstString(code, iterator, "  ");
+    Value nul = addConstString(code, iterator, "(null)");
+    Value primitive = addConstString(code, iterator, "(primitive)");
+    Value empty = addConstString(code, iterator, "");
+
+    iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, openParenthesis)));
+    for (int i = 0; i < arguments.size(); i++) {
+      iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, indent)));
+
+      // Add a block for end-of-line printing.
+      BasicBlock eol = BasicBlock.createGotoBlock(code.blocks.size());
+      code.blocks.add(eol);
+
+      BasicBlock successor = block.unlinkSingleSuccessor();
+      block.link(eol);
+      eol.link(successor);
+
+      Value argument = arguments.get(i);
+      if (argument.outType() != MoveType.OBJECT) {
+        iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, primitive)));
+      } else {
+        // Insert "if (argument != null) ...".
+        successor = block.unlinkSingleSuccessor();
+        If theIf = new If(If.Type.NE, argument);
+        BasicBlock ifBlock = BasicBlock.createIfBlock(code.blocks.size(), theIf);
+        code.blocks.add(ifBlock);
+        // Fallthrough block must be added right after the if.
+        BasicBlock isNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
+        code.blocks.add(isNullBlock);
+        BasicBlock isNotNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
+        code.blocks.add(isNotNullBlock);
+
+        // Link the added blocks together.
+        block.link(ifBlock);
+        ifBlock.link(isNotNullBlock);
+        ifBlock.link(isNullBlock);
+        isNotNullBlock.link(successor);
+        isNullBlock.link(successor);
+
+        // Fill code into the blocks.
+        iterator = isNullBlock.listIterator();
+        iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
+        iterator = isNotNullBlock.listIterator();
+        value = code.createValue(MoveType.OBJECT);
+        iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
+            ImmutableList.of(arguments.get(i))));
+        iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
+      }
+
+      iterator = eol.listIterator();
+      if (i == arguments.size() - 1) {
+        iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, closeParenthesis)));
+      } else {
+        iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, comma)));
+      }
+      block = eol;
+    }
+    // When we fall out of the loop the iterator is in the last eol block.
+    iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, empty)));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3f8d992..5945741 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -51,6 +51,7 @@
 
   public List<String> methodsFilter = ImmutableList.of();
   public int minApiLevel = Constants.DEFAULT_ANDROID_API;
+  public List<String> logArgumentsFilter = ImmutableList.of();
 
   // Defines interface method rewriter behavior.
   public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
@@ -110,6 +111,16 @@
     return methodsFilter.indexOf(qualifiedName) >= 0;
   }
 
+  public boolean methodMatchesLogArgumentsFilter(DexEncodedMethod method) {
+    // Not specifying a filter matches no methods.
+    if (logArgumentsFilter.size() == 0) {
+      return false;
+    }
+    // Currently the filter is simple string equality on the qualified name.
+    String qualifiedName = method.qualifiedName();
+    return logArgumentsFilter.indexOf(qualifiedName) >= 0;
+  }
+
   public static class OutlineOptions {
 
     public boolean enabled = true;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 333e77b..075c569 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -85,6 +85,17 @@
   }
 
   /**
+   * Compile an application with R8.
+   */
+  protected AndroidApp compileWithR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+    R8Command command =
+        ToolHelper.prepareR8CommandBuilder(app)
+            .build();
+    return ToolHelper.runR8(command, optionsConsumer);
+  }
+
+  /**
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 425dab7..d7fd468 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import java.io.IOException;
@@ -392,9 +393,16 @@
   }
 
   public static Path getClassFileForTestClass(Class clazz) {
-    String[] parts = clazz.getCanonicalName().split("\\.");
-    parts[parts.length - 1] = parts[parts.length - 1] + ".class";
-    return getClassPathForTests().resolve(Paths.get("", parts));
+    List<String> parts = Lists.newArrayList(clazz.getCanonicalName().split("\\."));
+    Class enclosing = clazz;
+    while (enclosing.getEnclosingClass() != null) {
+      parts.set(parts.size() - 2, parts.get(parts.size() - 2) + "$" + parts.get(parts.size() - 1));
+      parts.remove(parts.size() - 1);
+      enclosing = clazz.getEnclosingClass();
+    }
+    parts.set(parts.size() - 1, parts.get(parts.size() - 1) + ".class");
+    return getClassPathForTests().resolve(
+        Paths.get("", parts.toArray(new String[parts.size() - 1])));
   }
 
   public static DexApplication buildApplication(List<String> fileNames)
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
new file mode 100644
index 0000000..69db1ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/LogArgumentsTest.java
@@ -0,0 +1,78 @@
+// 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.rewrite.logarguments;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class LogArgumentsTest extends TestBase {
+
+  private int occourences(String match, String value) {
+    int count = 0;
+    int startIndex = 0;
+    while (true) {
+      int index = value.indexOf(match, startIndex);
+      if (index > 0) {
+        count++;
+      } else {
+        return count;
+      }
+      startIndex = index + match.length();
+    }
+  }
+
+  @Test
+  public void testStatic() throws Exception {
+    String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestStatic.a";
+    AndroidApp app = compileWithR8(
+        readClasses(TestStatic.class),
+        options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+    String result = runOnArt(app, TestStatic.class);
+    assertEquals(7, occourences(qualifiedMethodName, result));
+    assertEquals(3, occourences("(primitive)", result));
+    assertEquals(3, occourences("(null)", result));
+    assertEquals(1, occourences("java.lang.Object", result));
+    assertEquals(1, occourences("java.lang.Integer", result));
+    assertEquals(1, occourences("java.lang.String", result));
+  }
+
+  @Test
+  public void testInstance() throws Exception {
+    String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestInstance.a";
+    AndroidApp app = compileWithR8(
+        readClasses(TestInstance.class),
+        options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+    String result = runOnArt(app, TestInstance.class);
+    assertEquals(7, occourences(qualifiedMethodName, result));
+    assertEquals(7, occourences(
+        "class com.android.tools.r8.rewrite.logarguments.TestInstance", result));
+    assertEquals(3, occourences("(primitive)", result));
+    assertEquals(3, occourences("(null)", result));
+    assertEquals(1, occourences("java.lang.Object", result));
+    assertEquals(1, occourences("java.lang.Integer", result));
+    assertEquals(1, occourences("java.lang.String", result));
+  }
+
+  @Test
+  public void testInner() throws Exception {
+    String qualifiedMethodName = "com.android.tools.r8.rewrite.logarguments.TestInner$Inner.a";
+    AndroidApp app = compileWithR8(
+        readClasses(TestInner.class, TestInner.Inner.class),
+        options -> options.logArgumentsFilter = ImmutableList.of(qualifiedMethodName));
+    String result = runOnArt(app, TestInner.class);
+    assertEquals(7, occourences(qualifiedMethodName, result));
+    assertEquals(7, occourences(
+        "class com.android.tools.r8.rewrite.logarguments.TestInner$Inner", result));
+    assertEquals(3, occourences("(primitive)", result));
+    assertEquals(3, occourences("(null)", result));
+    assertEquals(1, occourences("java.lang.Object", result));
+    assertEquals(1, occourences("java.lang.Integer", result));
+    assertEquals(1, occourences("java.lang.String", result));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java
new file mode 100644
index 0000000..ad4bcbc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInner.java
@@ -0,0 +1,46 @@
+// 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.rewrite.logarguments;
+
+public class TestInner {
+
+  public class Inner {
+
+    public int a() {
+      return 0;
+    }
+
+    public int a(int x) {
+      return x;
+    }
+
+    public int a(int x, int y) {
+      return x + y;
+    }
+
+    public Object a(Object o) {
+      return null;
+    }
+
+    public Object a(Object o1, Object o2) {
+      return null;
+    }
+  }
+
+  private Inner createInner() {
+    return new Inner();
+  }
+
+  public static void main(String[] args) {
+    Inner inner = new TestInner().createInner();
+    inner.a();
+    inner.a(1);
+    inner.a(1, 2);
+    inner.a(new Object());
+    inner.a(null);
+    inner.a(new Integer(0), "");
+    inner.a(null, null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java
new file mode 100644
index 0000000..c08aa12
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestInstance.java
@@ -0,0 +1,39 @@
+// 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.rewrite.logarguments;
+
+public class TestInstance {
+
+  public int a() {
+    return 0;
+  }
+
+  public int a(int x) {
+    return x;
+  }
+
+  public int a(int x, int y) {
+    return x + y;
+  }
+
+  public Object a(Object o) {
+    return null;
+  }
+
+  public Object a(Object o1, Object o2) {
+    return null;
+  }
+
+  public static void main(String[] args) {
+    TestInstance test = new TestInstance();
+    test.a();
+    test.a(1);
+    test.a(1, 2);
+    test.a(new Object());
+    test.a(null);
+    test.a(new Integer(0), "");
+    test.a(null, null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java
new file mode 100644
index 0000000..d65a2f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/logarguments/TestStatic.java
@@ -0,0 +1,38 @@
+// 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.rewrite.logarguments;
+
+public class TestStatic {
+
+  public static int a() {
+    return 0;
+  }
+
+  public static int a(int x) {
+    return x;
+  }
+
+  public static int a(int x, int y) {
+    return x + y;
+  }
+
+  public static Object a(Object o) {
+    return null;
+  }
+
+  public static Object a(Object o1, Object o2) {
+    return null;
+  }
+
+  public static void main(String[] args) {
+    a();
+    a(1);
+    a(1, 2);
+    a(new Object());
+    a(null);
+    a(new Integer(0), "");
+    a(null, null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 6e03c0e..4530e2e 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
+
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
@@ -770,13 +772,13 @@
     if (dexComparator != null) {
       DexInspector ref = new DexInspector(Paths.get(originalDex));
       DexInspector inspector = new DexInspector(generated,
-          minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+          minify ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString() : null);
       dexComparator.accept(ref, inspector);
     }
 
     if (inspection != null) {
       DexInspector inspector = new DexInspector(generated,
-          minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+          minify ? temp.getRoot().toPath().resolve(DEFAULT_PROGUARD_MAP_FILE).toString() : null);
       inspection.accept(inspector);
     }
   }