Merge "Test desugaring dependencies of an android test"
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index ec4f527..5eb67c4 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -248,6 +248,10 @@
     }
 
     protected void validate() throws CompilationException {
+      if (app.hasMainDexList() && outputMode == OutputMode.FilePerClass) {
+        throw new CompilationException(
+            "Option --main-dex-list cannot be used with --file-per-class");
+      }
       FileUtils.validateOutputFile(outputPath);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 6ac5f1e..b856556 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -71,6 +71,14 @@
       return this;
     }
 
+    protected void validate() throws CompilationException {
+      super.validate();
+      if (getAppBuilder().hasMainDexList() && intermediate) {
+        throw new CompilationException(
+            "Option --main-dex-list cannot be used with --intermediate");
+      }
+    }
+
     /**
      * Build the final D8Command.
      */
@@ -95,17 +103,19 @@
       "Usage: d8 [options] <input-files>",
       " where <input-files> are any combination of dex, class, zip, jar, or apk files",
       " and options are:",
-      "  --debug             # Compile with debugging information (default).",
-      "  --release           # Compile without debugging information.",
-      "  --output <file>     # Output result in <outfile>.",
-      "                      # <file> must be an existing directory or a zip file.",
-      "  --lib <file>        # Add <file> as a library resource.",
-      "  --classpath <file>  # Add <file> as a classpath resource.",
-      "  --min-api           # Minimum Android API level compatibility",
-      "  --intermediate      # Compile an intermediate result intended for later merging.",
-      "  --file-per-class    # Produce a separate dex file per class",
-      "  --version           # Print the version of d8.",
-      "  --help              # Print this message."));
+      "  --debug                 # Compile with debugging information (default).",
+      "  --release               # Compile without debugging information.",
+      "  --output <file>         # Output result in <outfile>.",
+      "                          # <file> must be an existing directory or a zip file.",
+      "  --lib <file>            # Add <file> as a library resource.",
+      "  --classpath <file>      # Add <file> as a classpath resource.",
+      "  --min-api               # Minimum Android API level compatibility",
+      "  --intermediate          # Compile an intermediate result intended for later",
+      "                          # merging.",
+      "  --file-per-class        # Produce a separate dex file per class",
+      "  --main-dex-list <file>  # List of classes to place in the primary dex file.",
+      "  --version               # Print the version of d8.",
+      "  --help                  # Print this message."));
 
   private boolean intermediate = false;
 
@@ -121,6 +131,7 @@
   public static Builder parse(String[] args) throws CompilationException, IOException {
     CompilationMode modeSet = null;
     Path outputPath = null;
+    String mainDexList = null;
     Builder builder = builder();
     try {
       for (int i = 0; i < args.length; i++) {
@@ -156,6 +167,12 @@
           builder.addLibraryFiles(Paths.get(args[++i]));
         } else if (arg.equals("--classpath")) {
           builder.addClasspathFiles(Paths.get(args[++i]));
+        } else if (arg.equals("--main-dex-list")) {
+          if (mainDexList != null) {
+            throw new CompilationException("Only one --main-dex-list supported");
+          }
+          mainDexList = args[++i];
+          builder.setMainDexListFile(Paths.get(mainDexList));
         } else if (arg.equals("--min-api")) {
           builder.setMinApiLevel(Integer.valueOf(args[++i]));
         } else if (arg.equals("--intermediate")) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 3b88ba8..1f45dec 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -129,6 +129,14 @@
       return self();
     }
 
+    protected void validate() throws CompilationException {
+      super.validate();
+      if (minimalMainDex && mainDexRules.isEmpty() && !getAppBuilder().hasMainDexList()) {
+        throw new CompilationException(
+            "Option --minimal-main-dex require --main-dex-rules");
+      }
+    }
+
     @Override
     public R8Command build() throws CompilationException, IOException {
       // If printing versions ignore everything else.
@@ -193,20 +201,23 @@
       "Usage: r8 [options] <input-files>",
       " where <input-files> are any combination of dex, class, zip, jar, or apk files",
       " and options are:",
-      "  --release               # Compile without debugging information (default).",
-      "  --debug                 # Compile with debugging information.",
-      "  --output <file>         # Output result in <file>.",
-      "                          # <file> must be an existing directory or a zip file.",
-      "  --lib <file>            # Add <file> as a library resource.",
-      "  --min-api               # Minimum Android API level compatibility.",
-      "  --pg-conf <file>        # Proguard configuration <file> (implies tree shaking/minification).",
-      "  --pg-map <file>         # Proguard map <file>.",
-      "  --no-tree-shaking       # Force disable tree shaking of unreachable classes.",
-      "  --no-minification       # Force disable minification of names.",
-      "  --multidex-rules <file> # Enable automatic classes partitioning for legacy multidex.",
-      "                          # <file> is a Proguard configuration file (with only keep rules).",
-      "  --version               # Print the version of r8.",
-      "  --help                  # Print this message."));
+      "  --release                # Compile without debugging information (default).",
+      "  --debug                  # Compile with debugging information.",
+      "  --output <file>          # Output result in <file>.",
+      "                           # <file> must be an existing directory or a zip file.",
+      "  --lib <file>             # Add <file> as a library resource.",
+      "  --min-api                # Minimum Android API level compatibility.",
+      "  --pg-conf <file>         # Proguard configuration <file> (implies tree",
+      "                           # shaking/minification).",
+      "  --pg-map <file>          # Proguard map <file>.",
+      "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
+      "  --no-minification        # Force disable minification of names.",
+      "  --main-dex-rules <file>  # Proguard keep rules for classes to place in the",
+      "                           # primary dex file.",
+      "  --minimal-main-dex       # Only place classes specified by --main-dex-rules",
+      "                           # in the primary dex file.",
+      "  --version                # Print the version of r8.",
+      "  --help                   # Print this message."));
 
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
   private final boolean minimalMainDex;
@@ -271,9 +282,9 @@
         builder.setTreeShaking(false);
       } else if (arg.equals("--no-minification")) {
         builder.setMinification(false);
-      } else if (arg.equals("--multidex-rules")) {
+      } else if (arg.equals("--main-dex-rules")) {
         builder.addMainDexRules(Paths.get(args[++i]));
-      } else if (arg.equals("--minimal-maindex")) {
+      } else if (arg.equals("--minimal-main-dex")) {
         builder.setMinimalMainDex(true);
       } else if (arg.equals("--pg-conf")) {
         builder.addProguardConfigurationFiles(Paths.get(args[++i]));
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 44721ae..5c9d171 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
@@ -1107,7 +1107,7 @@
       catchSuccessor.splitCriticalExceptionEdges(
           code.valueNumberGenerator,
           newBlock -> {
-            newBlock.setNumber(code.blocks.size());
+            newBlock.setNumber(code.getHighestBlockNumber() + 1);
             blockIterator.add(newBlock);
           });
     }
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 6f1a9b2..398c87e 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
@@ -291,8 +291,37 @@
     }
 
     for (BasicBlock block : blocks) {
-      boolean blockEntry = true;
       ListIterator<Instruction> instructionIterator = block.listIterator();
+      // Close ranges up-to and including the first instruction. Ends are exclusive so the range is
+      // closed at entry.
+      int entryIndex = block.entry().getNumber();
+      {
+        ListIterator<LocalRange> it = openRanges.listIterator(0);
+        while (it.hasNext()) {
+          LocalRange openRange = it.next();
+          if (openRange.end <= entryIndex) {
+            it.remove();
+            assert currentLocals.get(openRange.register) == openRange.local;
+            currentLocals.remove(openRange.register);
+          }
+        }
+      }
+      // Open ranges up-to but excluding the first instruction. Starts are inclusive but entry is
+      // prior to the first instruction.
+      while (nextStartingRange != null && nextStartingRange.start < entryIndex) {
+        // If the range is live at this index open it.
+        if (entryIndex < nextStartingRange.end) {
+          openRanges.add(nextStartingRange);
+          assert !currentLocals.containsKey(nextStartingRange.register);
+          currentLocals.put(nextStartingRange.register, nextStartingRange.local);
+        }
+        nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
+      }
+      if (block.entry().isMoveException()) {
+        fixupSpillMovesAtMoveException(block, instructionIterator, openRanges, currentLocals);
+      } else {
+        block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(currentLocals));
+      }
       while (instructionIterator.hasNext()) {
         Instruction instruction = instructionIterator.next();
         int index = instruction.getNumber();
@@ -320,25 +349,15 @@
           }
           nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
         }
-
-        if (blockEntry) {
-          blockEntry = false;
-          if (instruction.isMoveException()) {
-            fixupSpillMovesAtMoveException(block, instructionIterator, openRanges, currentLocals);
-          } else {
-            block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(currentLocals));
-          }
-        } else {
-          if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
-            DebugLocalsChange change = createLocalsChange(ending, starting);
-            if (change != null) {
-              if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
-                instructionIterator.previous();
-                instructionIterator.add(change);
-                instructionIterator.next();
-              } else {
-                instructionIterator.add(change);
-              }
+        if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
+          DebugLocalsChange change = createLocalsChange(ending, starting);
+          if (change != null) {
+            if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
+              instructionIterator.previous();
+              instructionIterator.add(change);
+              instructionIterator.next();
+            } else {
+              instructionIterator.add(change);
             }
           }
         }
@@ -359,11 +378,13 @@
       initialLocals.put(exceptionalRegister, open.local);
     }
     block.setLocalsAtEntry(new Int2ReferenceOpenHashMap<>(initialLocals));
-    Int2ReferenceMap<DebugLocalInfo> clobberedLocals = new Int2ReferenceOpenHashMap<>();
-    Iterator<Instruction> moveIterator = block.iterator();
+    Instruction entry = instructionIterator.next();
+    assert block.entry() == entry;
     assert block.entry().isMoveException();
-    int index = block.entry().getNumber();
+    Iterator<Instruction> moveIterator = block.iterator();
     moveIterator.next();
+    int index = entry.getNumber();
+    Int2ReferenceMap<DebugLocalInfo> clobberedLocals = new Int2ReferenceOpenHashMap<>();
     while (moveIterator.hasNext()) {
       Instruction next = moveIterator.next();
       if (next.getNumber() != -1) {
@@ -392,10 +413,10 @@
     // Compute the final change in locals and emit it after all spill moves.
     while (instructionIterator.hasNext()) {
       if (instructionIterator.next().getNumber() != -1) {
-        instructionIterator.previous();
         break;
       }
     }
+    instructionIterator.previous();
     Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
     Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
     for (Entry<DebugLocalInfo> initialLocal : initialLocals.int2ReferenceEntrySet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
index eb66537..bd7efce 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SingleBlockSourceCode.java
@@ -155,6 +155,7 @@
     if (receiver != null) {
       receiverValue = builder.writeRegister(receiverRegister, MoveType.OBJECT, NO_THROW);
       builder.add(new Argument(receiverValue));
+      receiverValue.markAsThis();
     }
 
     // Fill in the Argument instructions in the argument block.
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 4aca460..86670b8 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -140,24 +140,21 @@
     if (holder != null && !holder.isLibraryClass()) {
       NamingState<DexProto> state = states
           .computeIfAbsent(type, k -> states.get(holder.superType).createChild());
-      assignNamesToMethods(holder.directMethods(), state, doPrivates, renaming);
-      assignNamesToMethods(holder.virtualMethods(), state, doPrivates, renaming);
+      holder.forEachMethod(method -> assignNameToMethod(method, state, doPrivates, renaming));
     }
     type.forAllExtendsSubtypes(
         subtype -> assignNamesToClassesMethods(subtype, doPrivates, renaming));
   }
 
-  private void assignNamesToMethods(DexEncodedMethod[] methods,
+  private void assignNameToMethod(DexEncodedMethod encodedMethod,
       NamingState<DexProto> state, boolean doPrivates, Map<DexMethod, DexString> renaming) {
-    for (DexEncodedMethod encodedMethod : methods) {
-      if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
-        continue;
-      }
-      DexMethod method = encodedMethod.method;
-      if (!state.isReserved(method.name, method.proto)
-          && !encodedMethod.accessFlags.isConstructor()) {
-        renaming.put(method, state.assignNewNameFor(method.name, method.proto, !doPrivates));
-      }
+    if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
+      return;
+    }
+    DexMethod method = encodedMethod.method;
+    if (!state.isReserved(method.name, method.proto)
+        && !encodedMethod.accessFlags.isConstructor()) {
+      renaming.put(method, state.assignNewNameFor(method.name, method.proto, !doPrivates));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index e767030..19d5153 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -579,6 +579,10 @@
       return this;
     }
 
+    public boolean hasMainDexList() {
+      return mainDexList != null;
+    }
+
     /**
      * Set the main-dex list data.
      */
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index a5e2309..8befa70 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -410,7 +410,7 @@
         R8Command.builder()
             .addProgramFiles(app)
             .setMainDexListFile(mainDexList)
-            .setMinimalMainDex(minimalMainDex)
+            .setMinimalMainDex(minimalMainDex && mainDex.size() > 0)
             .setOutputPath(outDir)
             .setTreeShaking(false)
             .setMinification(false)
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
index 6f45137..ed146b72 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -156,6 +156,37 @@
   }
 
   @Test
+  public void mainDexList() throws Throwable {
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString());
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
+  public void multipleMainDexList() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList1 = temp.getRoot().toPath().resolve("main-dex-list-1.txt");
+    Path mailDexList2 = temp.getRoot().toPath().resolve("main-dex-list-2.txt");
+    parse("--main-dex-list", mailDexList1.toString(), "--main-dex-list", mailDexList2.toString());
+  }
+
+  @Test
+  public void mainDexListWithFilePerClass() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString(), "--file-per-class");
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
+  public void mainDexListWithIntermediate() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path mailDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    D8Command command = parse("--main-dex-list", mailDexList.toString(), "--intermediate");
+    assertTrue(ToolHelper.getApp(command).hasMainDexList());
+  }
+
+  @Test
   public void invalidOutputFileTypeParse() throws Throwable {
     thrown.expect(CompilationException.class);
     Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
index 0c737b9..27f0367 100644
--- a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -89,6 +89,24 @@
   }
 
   @Test
+  public void mainDexRules() throws Throwable {
+    Path mailDexRules = temp.newFile("main-dex.rules").toPath();
+    parse("--main-dex-rules", mailDexRules.toString());
+  }
+
+  @Test
+  public void minimalMainDex() throws Throwable {
+    thrown.expect(CompilationException.class);
+    parse("--minimal-main-dex");
+  }
+
+  @Test
+  public void mainDexRulesWithMinimalMainDex() throws Throwable {
+    Path mailDexRules = temp.newFile("main-dex.rules").toPath();
+    parse("--main-dex-rules", mailDexRules.toString(), "--minimal-main-dex");
+  }
+
+  @Test
   public void existingOutputDirWithDexFiles() throws Throwable {
     Path existingDir = temp.newFolder().toPath();
     List<Path> classesFiles = ImmutableList.of(
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 1dabbd3..ee8981b 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -158,9 +158,9 @@
         app_provided_pg_conf = True
     if options.k:
       args.extend(['--pg-conf', options.k])
-    if 'multidexrules' in values:
-      for rules in values['multidexrules']:
-        args.extend(['--multidex-rules', rules])
+    if 'maindexrules' in values:
+      for rules in values['maindexrules']:
+        args.extend(['--main-dex-rules', rules])
 
   if not options.no_libraries and 'libraries' in values:
     for lib in values['libraries']:
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index a3d3791..114e6c7 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -77,7 +77,7 @@
       'pgconf': [
           '%s_proguard.config' % V12_22_PREFIX,
           '%s/proguardsettings/YouTubeRelease_proguard.config' % THIRD_PARTY],
-      'multidexrules' : [
+      'maindexrules' : [
           os.path.join(V12_22_BASE, 'mainDexClasses.rules'),
           os.path.join(V12_22_BASE, 'main-dex-classes-release.cfg'),
           os.path.join(V12_22_BASE, 'main_dex_YouTubeRelease_proguard.cfg')],