Merge "Rename IR Nop to DebugLocalReads and ensure it is never dead code."
diff --git a/.gitignore b/.gitignore
index 088d2b9..0090fb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,8 @@
 third_party/jdwp-tests
 third_party/kotlin.tar.gz
 third_party/kotlin
+third_party/shadow
+third_party/shadow.tar.gz
 third_party/photos/*
 !third_party/photos/*.sha1
 third_party/proguard/*
@@ -56,6 +58,7 @@
 gradle-app.setting
 gradlew
 gradlew.bat
+gradle/*
 #*#
 *~
 .#*
diff --git a/README.md b/README.md
index 1bd795c..5b40254 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,28 @@
 and test dependencies on the first run. This includes prebuilt version of the
 art runtime on which to validate the produced dex code.
 
+## Contributing
+
+In order to contribute to D8/R8 you have to sign the
+[Contributor License Agreement](https://cla.developers.google.com/about/google-individual).
+If your contribution is owned by your employer you need the
+[Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
+
+Once the license agreement is in place, you can upload your patches
+using 'git cl' which is available in depot_tools. Once you have a
+change that you are happy with you should make sure that it passes
+all tests and then upload the change to our code review tool using:
+
+    $ git cl upload
+
+On your first upload you will be asked to acquire credentials. Follow the
+instructions given by `git cl upload`.
+
+On successful uploads a link to the code review is printed in the
+output of the upload command. In the code review tool you can
+assign reviewers and mark the change ready for review. At that
+point the code review tool will send emails to reviewers.
+
 ## Getting help
 
 For questions, bug reports and other issues reach out to us at
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3d0cee7..45af2b3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.ir.optimize.SwitchMapCollector;
 import com.android.tools.r8.naming.Minifier;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.SourceFileRewriter;
 import com.android.tools.r8.optimize.BridgeMethodAnalysis;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
@@ -272,6 +273,11 @@
 
       application = optimize(application, appInfo, graphLense, executorService);
 
+      // Overwrite SourceFile if specified. This step should be done after IR conversion.
+      timing.begin("Rename SourceFile");
+      new SourceFileRewriter(appInfo, options).run();
+      timing.end();
+
       if (!options.mainDexKeepRules.isEmpty()) {
         appInfo = new AppInfoWithSubtyping(application);
         Enqueuer enqueuer = new Enqueuer(appInfo);
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e350c33..5de6538 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -22,7 +22,7 @@
   public final DexAccessFlags accessFlags;
   public DexType superType;
   public DexTypeList interfaces;
-  public final DexString sourceFile;
+  public DexString sourceFile;
   protected DexEncodedField[] staticFields;
   protected DexEncodedField[] instanceFields;
   protected DexEncodedMethod[] directMethods;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index db99359..9381c90 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -294,12 +294,20 @@
 
   public static class SetFile extends DexDebugEvent {
 
-    final DexString fileName;
+    DexString fileName;
 
     SetFile(DexString fileName) {
       this.fileName = fileName;
     }
 
+    public DexString getFileName() {
+      return fileName;
+    }
+
+    public void setFileName(DexString fileName) {
+      this.fileName = fileName;
+    }
+
     public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
       writer.putByte(Constants.DBG_SET_FILE);
       writer.putString(fileName);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index d2c9b3b..5236d12 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -12,7 +12,7 @@
 
   public final int startLine;
   public final DexString[] parameters;
-  public final DexDebugEvent[] events;
+  public DexDebugEvent[] events;
 
   public DexDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) {
     assert startLine >= 0;
diff --git a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
index 480e549..44ac9c8 100644
--- a/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/SmaliWriter.java
@@ -59,8 +59,9 @@
 
   @Override
   void writeMethod(DexEncodedMethod method, PrintStream ps) {
-    ps.println();
-    ps.println(method.toSmaliString(application.getProguardMap()));
+    ps.append("\n");
+    ps.append(method.toSmaliString(application.getProguardMap()));
+    ps.append("\n");
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 605eac3..61bd315 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -316,16 +316,12 @@
   }
 
   public void addDebugUser(Instruction user) {
-    if (isUninitializedLocal()) {
-      return;
-    }
+    assert getLocalInfo() != null;
     debugData.users.putIfAbsent(user, DebugUse.LIVE);
   }
 
   public void addDebugPhiUser(Phi user) {
-    if (isUninitializedLocal()) {
-      return;
-    }
+    assert getLocalInfo() != null;
     debugData.phiUsers.add(user);
   }
 
@@ -461,7 +457,7 @@
     if (isConstant || hasLocalInfo) {
       builder.append("(");
       if (isConstant) {
-        ConstNumber constNumber = getConstInstruction().asConstNumber();
+        ConstNumber constNumber = definition.asConstNumber();
         if (constNumber.outType() == MoveType.SINGLE) {
           builder.append((int) constNumber.getRawValue());
         } else {
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 fff55f0..25e5403 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
@@ -1163,7 +1163,9 @@
     List<Instruction> toInsertInThisBlock = new ArrayList<>();
     while (it.hasNext()) {
       Instruction instruction = it.next();
-      if (instruction.isConstNumber() && instruction.outValue().numberOfAllUsers() != 0) {
+      if (instruction.isConstNumber() &&
+          instruction.outValue().numberOfAllUsers() != 0 &&
+          instruction.outValue().getLocalInfo() == null) {
         // Collect the blocks for all users of the constant.
         List<BasicBlock> userBlocks = new LinkedList<>();
         for (Instruction user : instruction.outValue().uniqueUsers()) {
@@ -1476,7 +1478,6 @@
           assert instruction.inValues().size() == 1;
           Value inValue = instruction.inValues().get(0);
           if (inValue.definition != null &&
-              !inValue.definition.isConstNumber() &&
               !hasLineChangeBetween(inValue.definition, instruction) &&
               inValue.getLocalInfo() == null &&
               inValue.numberOfAllUsers() == 1) {
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
new file mode 100644
index 0000000..38f8cb3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -0,0 +1,66 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+
+/**
+ * Visit program {@link DexClass}es and replace their sourceFile with the given string.
+ *
+ * If -keepattribute SourceFile is not set, we rather remove that attribute.
+ */
+public class SourceFileRewriter {
+
+  private final AppInfo appInfo;
+  private final InternalOptions options;
+
+  public SourceFileRewriter(AppInfo appInfo, InternalOptions options) {
+    this.appInfo = appInfo;
+    this.options = options;
+  }
+
+  public void run() {
+    String renameSourceFile = options.proguardConfiguration.getRenameSourceFileAttribute();
+    // Return early if a user wants to keep the current source file attribute as-is.
+    if (renameSourceFile == null && options.keepAttributes.sourceFile) {
+      return;
+    }
+    // Now, the user wants either to remove source file attribute or to rename it.
+    DexString dexRenameSourceFile =
+        renameSourceFile == null
+            ? appInfo.dexItemFactory.createString("")
+            : appInfo.dexItemFactory.createString(renameSourceFile);
+    for (DexClass clazz : appInfo.classes()) {
+      clazz.sourceFile = dexRenameSourceFile;
+      clazz.forEachMethod(encodedMethod -> {
+        // Abstract methods do not have code_item.
+        if (encodedMethod.accessFlags.isAbstract()) {
+          return;
+        }
+        Code code = encodedMethod.getCode();
+        // Other kinds of {@link Code} do not have debug_info_item.
+        if (code == null || !code.isDexCode()) {
+          return;
+        }
+        if (code.asDexCode().getDebugInfo() == null) {
+          return;
+        }
+        // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely.
+        DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo();
+        dexDebugInfo.events =
+            Arrays.stream(dexDebugInfo.events)
+                .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile))
+                .toArray(DexDebugEvent[]::new);
+      });
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 47e809c..3eaa2be 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -26,6 +26,7 @@
   public static final String JAR_EXTENSION = ".jar";
   public static final String ZIP_EXTENSION = ".zip";
   public static final String DEFAULT_DEX_FILENAME = "classes.dex";
+  public static final String JAVA_EXTENSION = ".java";
 
   public static boolean isDexFile(Path path) {
     String name = path.getFileName().toString().toLowerCase();
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index e936be6..4155dd2 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -157,37 +157,43 @@
 
   // Tests that are flaky with the Art version we currently use.
   // TODO(zerny): Amend flaky tests with an expected flaky result to track issues.
-  private static List<String> flakyRunWithArt = ImmutableList.of(
-      // Can crash but mostly passes
-      // Crashes:
-      // check_reference_map_visitor.h:44] At Main.f
-      // Check failed: size() >= sizeof(T) (size()=0, sizeof(T)=1)
-      // If art is passed -Xrelocate instead of -Xnorelocate the test passes.
-      "004-ReferenceMap",
-      // Also marked flaky in the art repo, sometimes hangs, sometimes fails with a segfault:
-      // line 105: 23283 Segmentation fault
-      "004-ThreadStress",
-      // When it fails:
-      // stack_walk_jni.cc:57] Check failed: 0xdU == GetDexPc() (0xdU=13, GetDexPc()=23)
-      // The R8/D8 code does not produce code with the same instructions.
-      "004-StackWalk",
-      // Nothing ensures the weak-ref is not cleared between makeRef and printWeakReference on lines
-      // Main.java:78-79. An expected flaky result contains: "but was:<wimp: [null]".
-      "036-finalizer",
-      // Can lead to art-master crash: concurrent_copying.cc:2135] Check failed:
-      // to_ref != nullptr Fall-back non-moving space allocation failed
-      "080-oom-fragmentation",
-      // Seen crash: currently no more information
-      "144-static-field-sigquit",
-      // Opens a lot of file descriptors and depending on the state of the machine this
-      // can crash art or not. Skip the run on art.
-      "151-OpenFileLimit",
-      // Can cause a segfault in the art vm 7.0.0
-      // tools/linux/art-7.0.0/bin/art: line 105: 14395 Segmentation fault
-      "607-daemon-stress",
-      // Marked as flaky in the Art repository.
-      "149-suspend-all-stress"
-  );
+  private static Multimap<String, TestCondition> flakyRunWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // Can crash but mostly passes
+          // Crashes:
+          // check_reference_map_visitor.h:44] At Main.f
+          // Check failed: size() >= sizeof(T) (size()=0, sizeof(T)=1)
+          // If art is passed -Xrelocate instead of -Xnorelocate the test passes.
+          .put("004-ReferenceMap", TestCondition.any())
+          // Also marked flaky in the art repo, sometimes hangs, sometimes fails with a segfault:
+          // line 105: 23283 Segmentation fault
+          .put("004-ThreadStress", TestCondition.any())
+          // When it fails:
+          // stack_walk_jni.cc:57] Check failed: 0xdU == GetDexPc() (0xdU=13, GetDexPc()=23)
+          // The R8/D8 code does not produce code with the same instructions.
+          .put("004-StackWalk", TestCondition.any())
+          // Nothing ensures the weak-ref is not cleared between makeRef and
+          // printWeakReference on lines Main.java:78-79. An expected flaky
+          // result contains: "but was:<wimp: [null]".
+          .put("036-finalizer", TestCondition.any())
+          // Can lead to art-master crash: concurrent_copying.cc:2135] Check failed:
+          // to_ref != nullptr Fall-back non-moving space allocation failed
+          .put("080-oom-fragmentation", TestCondition.any())
+          // Failed on buildbot with: terminate called after throwing an instance
+          // of '__gnu_cxx::recursive_init_error'
+          .put("096-array-copy-concurrent-gc",
+              TestCondition.match(TestCondition.runtimes(DexVm.ART_4_4_4)))
+          // Seen crash: currently no more information
+          .put("144-static-field-sigquit", TestCondition.any())
+          // Opens a lot of file descriptors and depending on the state of the machine this
+          // can crash art or not. Skip the run on art.
+          .put("151-OpenFileLimit", TestCondition.any())
+          // Can cause a segfault in the art vm 7.0.0
+          // tools/linux/art-7.0.0/bin/art: line 105: 14395 Segmentation fault
+          .put("607-daemon-stress", TestCondition.any())
+          // Marked as flaky in the Art repository.
+          .put("149-suspend-all-stress", TestCondition.any())
+          .build();
 
   // Tests that are never compiled or run.
   private static List<String> skipAltogether = ImmutableList.of(
@@ -1030,8 +1036,7 @@
     Map<SpecificationKey, TestSpecification> data = new HashMap<>();
 
     // Collect tests where running Art is skipped (we still run R8/D8 on these).
-    Set<String> skipArt = Sets.newHashSet(flakyRunWithArt);
-    skipArt.addAll(customRun);
+    Set<String> skipArt = new HashSet<>(customRun);
 
     Set<String> skipTest = Sets.newHashSet(skipAltogether);
     skipTest.addAll(usesNativeAgentCode);
@@ -1046,6 +1051,10 @@
           collectTestsMatchingConditions(
               dexTool, compilerUnderTest, dexVm, compilationMode, failingWithCompiler);
 
+      // Collect the tests that are flaky.
+      skipArt.addAll(collectTestsMatchingConditions(
+              dexTool, compilerUnderTest, dexVm, compilationMode, flakyRunWithArt));
+
       // Collect tests that has no input:
       if (dexTool == DexTool.NONE) {
         skipTest.addAll(noInputJar);
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 3c40b74..6f6b7d0 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -19,7 +19,7 @@
   public static void setUp() throws Exception {
     // Force inversion of all conditionals to reliably construct a regression test for incorrect
     // line information when reording blocks.
-    setUp(options -> options.testing.invertConditionals = true);
+    setUp(options -> options.testing.invertConditionals = true, null);
   }
 
   @Test
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 1cd5c71..1f9101c 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -6,10 +6,13 @@
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -29,6 +32,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -104,6 +108,7 @@
   public static TemporaryFolder temp = new TemporaryFolder();
   private static Path jdwpDexD8 = null;
   private static Path debuggeeDexD8 = null;
+  private static Path debuggeeDexR8 = null;
   private static Path debuggeeJava8DexD8 = null;
   private static Path debuggeeKotlinDexD8 = null;
 
@@ -112,25 +117,30 @@
 
   @BeforeClass
   public static void setUp() throws Exception {
-    setUp(null);
+    setUp(null, null);
   }
 
-  protected static void setUp(Consumer<InternalOptions> optionsConsumer) throws Exception {
+  protected static void setUp(
+      Consumer<InternalOptions> optionsConsumer,
+      Consumer<ProguardConfiguration.Builder> pgConsumer)
+      throws Exception {
     // Convert jar to dex with d8 with debug info
     jdwpDexD8 = compileToDex(null, JDWP_JAR);
+    // TODO(zerny): supply a set of compilers to run with.
     debuggeeDexD8 = compileToDex(optionsConsumer, DEBUGGEE_JAR);
+    debuggeeDexR8 = compileToDexViaR8(optionsConsumer, pgConsumer, DEBUGGEE_JAR);
     debuggeeJava8DexD8 = compileToDex(options -> {
-          // Enable desugaring for preN runtimes
-          options.interfaceMethodDesugaring = OffOrAuto.Auto;
-          if (optionsConsumer != null) {
-            optionsConsumer.accept(options);
-          }
-        }, DEBUGGEE_JAVA8_JAR);
+      // Enable desugaring for preN runtimes
+      options.interfaceMethodDesugaring = OffOrAuto.Auto;
+      if (optionsConsumer != null) {
+        optionsConsumer.accept(options);
+      }
+    }, DEBUGGEE_JAVA8_JAR);
     debuggeeKotlinDexD8 = compileToDex(optionsConsumer, DEBUGGEE_KOTLIN_JAR);
   }
 
-  protected static Path compileToDex(Consumer<InternalOptions> optionsConsumer,
-      Path jarToCompile) throws IOException, CompilationException {
+  static Path compileToDex(Consumer<InternalOptions> optionsConsumer, Path jarToCompile)
+      throws IOException, CompilationException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
     Path dexOutputDir = temp.newFolder().toPath();
@@ -144,7 +154,31 @@
             .build(),
         optionsConsumer);
     return dexOutputDir.resolve("classes.dex");
+  }
 
+  static Path compileToDexViaR8(
+      Consumer<InternalOptions> optionsConsumer,
+      Consumer<ProguardConfiguration.Builder> pgConsumer,
+      Path jarToCompile)
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    assert jarToCompile.toFile().exists();
+    Path dexOutputDir = temp.newFolder().toPath();
+    R8Command.Builder builder =
+        R8Command.builder()
+            .addProgramFiles(jarToCompile)
+            .setOutputPath(dexOutputDir)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)));
+    if (pgConsumer != null) {
+      builder
+          .addProguardConfiguration(
+              ImmutableList.of("-keepattributes SourceFile,LineNumberTable"))
+          .addProguardConfigurationConsumer(pgConsumer);
+    }
+    ToolHelper.runR8(builder.build(), optionsConsumer);
+    return dexOutputDir.resolve("classes.dex");
   }
 
   protected final boolean supportsDefaultMethod() {
@@ -200,6 +234,16 @@
     runDebugTest(Collections.emptyList(), LanguageFeatures.KOTLIN, debuggeeClass, commands);
   }
 
+  protected final void runDebugTestR8(String debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runDebugTestR8(debuggeeClass, Arrays.asList(commands));
+  }
+
+  protected final void runDebugTestR8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
+    runDebugTest(Collections.emptyList(), LanguageFeatures.R8, debuggeeClass, commands);
+  }
+
   protected enum LanguageFeatures {
     JAVA_7(DEBUGGEE_JAR) {
       @Override
@@ -218,6 +262,12 @@
       public Path getDexPath() {
         return debuggeeKotlinDexD8;
       }
+    },
+    R8(DEBUGGEE_JAR) {
+      @Override
+      public Path getDexPath() {
+        return debuggeeDexR8;
+      }
     };
 
     private final Path jarPath;
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
new file mode 100644
index 0000000..d0ea94c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -0,0 +1,74 @@
+// 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.naming;
+
+import com.android.tools.r8.debug.DebugTestBase;
+import com.android.tools.r8.shaking.ProguardKeepRule;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Tests -renamesourcefileattribute.
+ */
+public class RenameSourceFileDebugTest extends DebugTestBase {
+
+  private static final String TEST_FILE = "TestFile.java";
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    setUp(null, pg -> {
+      pg.addRule(ProguardKeepRule.defaultKeepAllRule());
+      pg.setRenameSourceFileAttribute(TEST_FILE);
+    });
+  }
+
+  /**
+   * replica of {@link ClassInitializationTest#testBreakpointInEmptyClassInitializer}
+   */
+  @Test
+  public void testBreakpointInEmptyClassInitializer() throws Throwable {
+    final String CLASS = "ClassInitializerEmpty";
+    runDebugTestR8(CLASS,
+        breakpoint(CLASS, "<clinit>"),
+        run(),
+        checkLine(TEST_FILE, 8),
+        run());
+  }
+
+  /**
+   * replica of {@link LocalsTest#testNoLocal}, except for checking overwritten class file.
+   */
+  @Test
+  public void testNoLocal() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "noLocals";
+    runDebugTestR8(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(TEST_FILE, 8),
+        checkNoLocal(),
+        stepOver(),
+        checkMethod(className, methodName),
+        checkLine(TEST_FILE, 9),
+        checkNoLocal(),
+        run());
+  }
+
+  /**
+   * replica of {@link MultipleReturnsTest#testMultipleReturns}
+   */
+  @Test
+  public void testMultipleReturns() throws Throwable {
+    runDebugTestR8("MultipleReturns",
+        breakpoint("MultipleReturns", "multipleReturns"),
+        run(),
+        stepOver(),
+        checkLine(TEST_FILE, 16), // this should be the 1st return statement
+        run(),
+        stepOver(),
+        checkLine(TEST_FILE, 18), // this should be the 2nd return statement
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
new file mode 100644
index 0000000..4506310
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java
@@ -0,0 +1,123 @@
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests -renamesourcefileattribute.
+ */
+@RunWith(Parameterized.class)
+public class RenameSourceFileSmaliTest extends SmaliTestBase {
+
+  private static final String TEST_FILE = "TestFile.java";
+
+  private static final List<String> DEFAULT_PG_CONFIGS =
+      ImmutableList.of(
+          "-keep class *** { *; }",
+          "-dontoptimize",
+          "-keepattributes SourceFile,LineNumberTable");
+
+  private void configure(ProguardConfiguration.Builder pg) {
+    if (renaming) {
+      pg.setRenameSourceFileAttribute(TEST_FILE);
+    }
+  }
+
+  @Parameter
+  public boolean renaming;
+
+  @Parameters(name="renaming:{0}")
+  public static Object[] parameters() {
+    return new Object[] {true, false};
+  }
+
+  /**
+   * replica of {@link RunArtSmokeTest#test}
+   */
+  @Test
+  public void artSmokeTest() throws Exception {
+    // Build simple "Hello, world!" application.
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    String originalSourceFile = DEFAULT_CLASS_NAME + FileUtils.JAVA_EXTENSION;
+    builder.setSourceFile(originalSourceFile);
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        ".line 1",
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const-string        v1, \"Hello, world!\"",
+        ".source \"PrintStream.java\"",
+        ".line 337",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        ".source \"" + originalSourceFile + "\"",
+        ".line 2",
+        "    return-void"
+    );
+    Path processedApp = runR8(builder, DEFAULT_PG_CONFIGS, this::configure, null);
+
+    DexClass mainClass = getClass(processedApp, DEFAULT_CLASS_NAME);
+    verifySourceFileInCodeItem(mainClass, originalSourceFile, TEST_FILE);
+
+    DexEncodedMethod mainMethod = getMethod(processedApp, mainSignature);
+    assertNotNull(mainMethod);
+
+    DexCode code = mainMethod.getCode().asDexCode();
+    assertTrue(code.instructions[0] instanceof SgetObject);
+    assertTrue(code.instructions[1] instanceof ConstString);
+    assertTrue(code.instructions[2] instanceof InvokeVirtual);
+    assertTrue(code.instructions[3] instanceof ReturnVoid);
+
+    // Run the generated code in Art.
+    String result = runArt(processedApp, DEFAULT_MAIN_CLASS_NAME);
+    assertEquals(StringUtils.lines("Hello, world!"), result);
+
+    verifySourceFileInDebugInfo(code);
+  }
+
+  private void verifySourceFileInCodeItem(DexClass clazz, String original, String rename) {
+    String processedSourceFile = clazz.sourceFile.toString();
+    if (renaming) {
+      assertEquals(rename, processedSourceFile);
+    } else {
+      assertEquals(original, processedSourceFile);
+    }
+  }
+
+  private void verifySourceFileInDebugInfo(DexCode code) {
+    assertNotNull(code.getDebugInfo());
+    assertNotEquals(0, code.getDebugInfo().events.length);
+    long setFileCount =
+        Arrays.stream(code.getDebugInfo().events)
+            .filter(dexDebugEvent -> dexDebugEvent instanceof SetFile)
+            .count();
+    if (renaming) {
+      assertEquals(0, setFileCount);
+    } else {
+      assertNotEquals(0, setFileCount);
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
index 6a39c6d..214a3b4 100644
--- a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
@@ -5,9 +5,9 @@
 package com.android.tools.r8.smali;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.InvokeVirtual;
 import com.android.tools.r8.code.ReturnVoid;
@@ -41,7 +41,7 @@
 
     // Return the processed method for inspection.
     DexEncodedMethod main = getMethod(processedApplication, mainSignature);
-    assert main != null;
+    assertNotNull(main);
 
     DexCode code = main.getCode().asDexCode();
     assertTrue(code.instructions[0] instanceof SgetObject);
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index c580f74..f2f1220 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -7,7 +7,9 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
@@ -24,6 +26,8 @@
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
@@ -32,11 +36,13 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OutputMode;
 import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -45,6 +51,7 @@
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 import org.antlr.runtime.RecognitionException;
 
 public class SmaliTestBase extends TestBase {
@@ -71,6 +78,12 @@
     public static MethodSignature staticInitializer(String clazz) {
       return new MethodSignature(clazz, "<clinit>", "void", ImmutableList.of());
     }
+
+    @Override
+    public String toString() {
+      return returnType + " " + clazz + "." + name
+          + "(" + StringUtils.join(parameterTypes, ",") + ")";
+    }
   }
 
   public static class SmaliBuilder {
@@ -468,6 +481,30 @@
     }
   }
 
+  protected Path runR8(
+      SmaliBuilder builder,
+      List<String> proguardConfigurations,
+      Consumer<ProguardConfiguration.Builder> pgConsumer,
+      Consumer<InternalOptions> optionsConsumer) {
+    try {
+      Path dexOutputDir = temp.newFolder().toPath();
+      R8Command command =
+          R8Command.builder()
+              .addDexProgramData(builder.compile())
+              .setOutputPath(dexOutputDir)
+              .setMode(CompilationMode.DEBUG)
+              .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+              .addProguardConfiguration(proguardConfigurations)
+              .addProguardConfigurationConsumer(pgConsumer)
+              .build();
+      ToolHelper.runR8WithFullResult(command, optionsConsumer);
+      return dexOutputDir.resolve("classes.dex");
+    } catch (CompilationException | IOException | RecognitionException | ExecutionException
+        | ProguardRuleParserException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   protected DexClass getClass(DexApplication application, String className) {
     DexInspector inspector = new DexInspector(application);
     ClassSubject clazz = inspector.clazz(className);
@@ -479,9 +516,23 @@
     return getClass(application, signature.clazz);
   }
 
-  protected DexEncodedMethod getMethod(DexApplication application, String className,
-      String returnType, String methodName, List<String> parameters) {
-    DexInspector inspector = new DexInspector(application);
+  protected DexClass getClass(Path appPath, String className) {
+    try {
+      DexInspector inspector = new DexInspector(appPath);
+      ClassSubject clazz = inspector.clazz(className);
+      assertTrue(clazz.isPresent());
+      return clazz.getDexClass();
+    } catch (IOException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexEncodedMethod getMethod(
+      DexInspector inspector,
+      String className,
+      String returnType,
+      String methodName,
+      List<String> parameters) {
     ClassSubject clazz = inspector.clazz(className);
     assertTrue(clazz.isPresent());
     MethodSubject method = clazz.method(returnType, methodName, parameters);
@@ -489,6 +540,30 @@
     return method.getMethod();
   }
 
+  protected DexEncodedMethod getMethod(
+      DexApplication application,
+      String className,
+      String returnType,
+      String methodName,
+      List<String> parameters) {
+    DexInspector inspector = new DexInspector(application);
+    return getMethod(inspector, className, returnType, methodName, parameters);
+  }
+
+  protected DexEncodedMethod getMethod(Path appPath, MethodSignature signature) {
+    try {
+      DexInspector inspector = new DexInspector(appPath);
+      return getMethod(
+          inspector,
+          signature.clazz,
+          signature.returnType,
+          signature.name,
+          signature.parameterTypes);
+    } catch (IOException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   protected DexEncodedMethod getMethod(DexApplication application, MethodSignature signature) {
     return getMethod(application,
         signature.clazz, signature.returnType, signature.name, signature.parameterTypes);
@@ -563,6 +638,14 @@
     }
   }
 
+  public static String runArt(Path path, String mainClass) {
+    try {
+      return ToolHelper.runArtNoVerificationErrors(path.toString(), mainClass);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
   public void runDex2Oat(DexApplication application, InternalOptions options)
       throws DexOverflowException {
     try {
diff --git a/tools/archive.py b/tools/archive.py
index 3d4523b..0ae7233 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -68,7 +68,7 @@
     print 'On master, using git hash for archiving'
     version = GetGitHash()
 
-  for jar in [utils.D8_JAR, utils.R8_JAR]:
+  for jar in [utils.D8_JAR, utils.R8_JAR, utils.COMPATDX_JAR]:
     file_name = os.path.basename(jar)
     destination = GetUploadDestination(version, file_name, is_master)
     print('Uploading %s to %s' % (jar, destination))
diff --git a/tools/utils.py b/tools/utils.py
index 4812c4a..18f9779 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -19,8 +19,10 @@
     'dexsegments.jar')
 DEX_SEGMENTS_RESULT_PATTERN = re.compile('- ([^:]+): ([0-9]+)')
 COMPATDX_JAR = os.path.join(REPO_ROOT, 'build', 'libs', 'compatdx.jar')
-D8_JAR = os.path.join(REPO_ROOT, 'build', 'libs', 'd8.jar')
-R8_JAR = os.path.join(REPO_ROOT, 'build', 'libs', 'r8.jar')
+LIBS = os.path.join(REPO_ROOT, 'build', 'libs')
+D8_JAR = os.path.join(LIBS, 'd8.jar')
+R8_JAR = os.path.join(LIBS, 'r8.jar')
+COMPATDX_JAR = os.path.join(LIBS, 'compatdx.jar')
 
 def PrintCmd(s):
   if type(s) is list: