Merge "Allow to use error prone"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index e025665..ab46628 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -108,7 +108,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      System.out.println("D8 " + Version.LABEL);
+      Version.printToolVersion("D8");
       return;
     }
     run(command);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 27e7d02..62e3413 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -504,7 +504,7 @@
       return;
     }
     if (command.isPrintVersion()) {
-      System.out.println("R8 " + Version.LABEL);
+      Version.printToolVersion("R8");
       return;
     }
     run(command);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 9a11817..564aa9c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -43,8 +43,8 @@
       super(CompilationMode.RELEASE);
     }
 
-    protected Builder(boolean forceProguardCompatibility) {
-      super(CompilationMode.RELEASE);
+    protected Builder(boolean ignoreDexInArchive, boolean forceProguardCompatibility) {
+      super(CompilationMode.RELEASE, ignoreDexInArchive);
       this.forceProguardCompatibility = forceProguardCompatibility;
     }
 
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index ed19d71..c59c205 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -4,10 +4,25 @@
 
 package com.android.tools.r8;
 
+import com.android.tools.r8.utils.VersionProperties;
+import java.io.IOException;
+
 public final class Version {
 
   public static final String LABEL = "v0.2.0-dev";
 
   private Version() {
   }
+
+  public static void printToolVersion(String toolName) {
+    System.out.println(toolName + " " + Version.LABEL);
+    try {
+      VersionProperties version =
+          new VersionProperties(Version.class.getClassLoader());
+      System.out.println(version.getDescription());
+    } catch (IOException e) {
+      System.out.println("eng build");
+    }
+
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 905b356..09f3321 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.D8Output;
 import com.android.tools.r8.Resource;
+import com.android.tools.r8.Version;
 import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
 import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
 import com.android.tools.r8.errors.CompilationError;
@@ -62,6 +63,7 @@
     // Final values after parsing.
     // Note: These are ordered by their occurrence in "dx --help"
     public final boolean help;
+    public final boolean version;
     public final boolean debug;
     public final boolean verbose;
     public final PositionInfo positions;
@@ -165,6 +167,7 @@
       final OptionSpec<Integer> minApiLevel;
       final OptionSpec<String> inputList;
       final OptionSpec<String> inputs;
+      final OptionSpec<Void> version;
       final OptionSpec<Void> help;
       final OptionSpec<Integer> maxIndexNumber;
 
@@ -246,12 +249,14 @@
             .withRequiredArg()
             .describedAs(FILE_ARG);
         inputs = parser.nonOptions("Input files");
+        version = parser.accepts("version", "Print the version of this tool").forHelp();
         help = parser.accepts("help", "Print this message").forHelp();
       }
     }
 
     private DxCompatOptions(OptionSet options, Spec spec) {
       help = options.has(spec.help);
+      version = options.has(spec.version);
       debug = options.has(spec.debug);
       verbose = options.has(spec.verbose);
       if (options.has(spec.positions)) {
@@ -336,6 +341,10 @@
       printHelpOn(System.out);
       return;
     }
+    if (dexArgs.version) {
+      Version.printToolVersion("CompatDx");
+      return;
+    }
     CompilationMode mode = CompilationMode.RELEASE;
     Path output = null;
     List<Path> inputs = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 9dc5424..75c28d1 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.Version;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Paths;
@@ -71,6 +72,10 @@
   }
 
   private static void run(String[] args) throws IOException, CompilationException {
+    if (args.length == 0) {
+      Version.printToolVersion("CompatProguard");
+      return;
+    }
     System.out.println("CompatProguard " + String.join(" ", args));
     // Run R8 passing all the options from the command line as a Proguard configuration.
     CompatProguardOptions options = CompatProguardOptions.parse(args);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
index df2298c..145d0e6 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguardCommandBuilder.java
@@ -8,6 +8,6 @@
 
 public class CompatProguardCommandBuilder extends R8Command.Builder {
   CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
-    super(forceProguardCompatibility);
+    super(true, forceProguardCompatibility);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 7f640c3..75967cf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -43,12 +43,10 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Queue;
 import java.util.Set;
 import org.objectweb.asm.Handle;
@@ -191,34 +189,11 @@
     this.application = application;
     this.clazz = clazz;
     parameterTypes = Arrays.asList(Type.getArgumentTypes(node.desc));
-    state = new JarState(node.maxLocals, computeLocals(node.localVariables, application));
+    state = new JarState(node.maxLocals, node.localVariables, application);
     AbstractInsnNode first = node.instructions.getFirst();
     initialLabel = first instanceof LabelNode ? (LabelNode) first : null;
   }
 
-  private static Map<LocalVariableNode, DebugLocalInfo> computeLocals(
-      List localNodes, JarApplicationReader application) {
-    Map<DebugLocalInfo, DebugLocalInfo> canonical = new HashMap<>(localNodes.size());
-    Map<LocalVariableNode, DebugLocalInfo> localVariables = new HashMap<>(localNodes.size());
-    for (Object o : localNodes) {
-      LocalVariableNode node = (LocalVariableNode) o;
-      localVariables.computeIfAbsent(node, n -> canonicalizeLocal(n, canonical, application));
-    }
-    return localVariables;
-  }
-
-  private static DebugLocalInfo canonicalizeLocal(
-      LocalVariableNode node,
-      Map<DebugLocalInfo, DebugLocalInfo> canonicalLocalVariables,
-      JarApplicationReader application) {
-    DebugLocalInfo info = new DebugLocalInfo(
-        application.getString(node.name),
-        application.getTypeFromDescriptor(node.desc),
-        node.signature == null ? null : application.getString(node.signature));
-    DebugLocalInfo canonical = canonicalLocalVariables.putIfAbsent(info, info);
-    return canonical != null ? canonical : info;
-  }
-
   private boolean isStatic() {
     return (node.access & Opcodes.ACC_STATIC) > 0;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 6faba19..d211a03 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -5,7 +5,9 @@
 
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.JarApplicationReader;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.base.Equivalence;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Multimap;
@@ -13,6 +15,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Deque;
 import java.util.HashMap;
@@ -42,6 +45,26 @@
   // Type representative for a value that may be either a boolean or a byte.
   public static final Type BYTE_OR_BOOL_TYPE = null;
 
+  // Equivalence to canonicalize local-variable information.
+  private static class LocalNodeEquivalence extends Equivalence<LocalVariableNode> {
+
+    @Override
+    protected boolean doEquivalent(LocalVariableNode a, LocalVariableNode b) {
+      if (!a.name.equals(b.name) || !a.desc.equals(b.desc)) {
+        return false;
+      }
+      return (a.signature == null && b.signature == null)
+          || (a.signature != null && a.signature.equals(b.signature));
+    }
+
+    @Override
+    protected int doHash(LocalVariableNode local) {
+      return 31 * local.name.hashCode()
+          + 7 * local.desc.hashCode()
+          + (local.signature == null ? 0 : local.signature.hashCode());
+    }
+  }
+
   // Typed mapping from a local slot or stack slot to a virtual register.
   public static class Slot {
     public final int register;
@@ -183,7 +206,8 @@
   private final Local[] locals;
 
   // Mapping from local-variable nodes to their canonical local info.
-  private final Map<LocalVariableNode, DebugLocalInfo> localVariables;
+  private final Map<Equivalence.Wrapper<LocalVariableNode>, DebugLocalInfo> localVariables;
+  private final LocalNodeEquivalence localNodeEquivalence = new LocalNodeEquivalence();
 
   // Scope-points of all local variables for inserting debug scoping instructions.
   private final Multimap<LabelNode, LocalVariableNode> localVariableStartPoints;
@@ -198,20 +222,54 @@
   // Concretely we treat all remaining byte-or-bool types as bytes (no actual type can flow there).
   private boolean building = false;
 
-  public JarState(int maxLocals, Map<LocalVariableNode, DebugLocalInfo> localVariables) {
+  // TODO(zerny): Precompute the necessary local structures for faster access and remove this.
+  private final List localNodes;
+
+  public JarState(int maxLocals, List localNodes, JarApplicationReader application) {
     int localsRegistersSize = maxLocals * 3;
     localsSize = maxLocals;
     locals = new Local[localsRegistersSize];
     startOfStack = localsRegistersSize;
     topOfStack = startOfStack;
-    this.localVariables = localVariables;
+    this.localNodes = localNodes;
+    localVariables = computeLocals(localNodes, application);
     localVariableStartPoints = HashMultimap.create();
     localVariableEndPoints = HashMultimap.create();
-    populateLocalTables();
+    populateLocalTables(localNodes);
   }
 
-  private void populateLocalTables() {
-    for (LocalVariableNode node : localVariables.keySet()) {
+  private Map<Equivalence.Wrapper<LocalVariableNode>, DebugLocalInfo> computeLocals(
+      List localNodes, JarApplicationReader application) {
+    int size = localNodes.size();
+    if (size == 0) {
+      return Collections.emptyMap();
+    }
+    if (size == 1) {
+      LocalVariableNode local = (LocalVariableNode) localNodes.get(0);
+      return Collections.singletonMap(
+          localNodeEquivalence.wrap(local), createLocalInfo(local, application));
+    }
+    Map<Equivalence.Wrapper<LocalVariableNode>, DebugLocalInfo> locals = new HashMap<>(size);
+    for (Object o : localNodes) {
+      LocalVariableNode node = (LocalVariableNode) o;
+      Equivalence.Wrapper<LocalVariableNode> wrapped = localNodeEquivalence.wrap(node);
+      locals.computeIfAbsent(wrapped, w -> createLocalInfo(w.get(), application));
+    }
+    return locals;
+  }
+
+  private static DebugLocalInfo createLocalInfo(
+      LocalVariableNode node,
+      JarApplicationReader application) {
+    return new DebugLocalInfo(
+        application.getString(node.name),
+        application.getTypeFromDescriptor(node.desc),
+        node.signature == null ? null : application.getString(node.signature));
+  }
+
+  private void populateLocalTables(List localNodes) {
+    for (Object object : localNodes) {
+      LocalVariableNode node = (LocalVariableNode) object;
       if (node.start != node.end) {
         localVariableStartPoints.put(node.start, node);
         localVariableEndPoints.put(node.end, node);
@@ -221,9 +279,10 @@
 
   public boolean localLiveAt(Local local, int offset, JarSourceCode source) {
     // TODO(zerny): Precompute and sort the local ranges.
-    for (Entry<LocalVariableNode, DebugLocalInfo> entry : localVariables.entrySet()) {
-      LocalVariableNode node = entry.getKey();
-      if (entry.getValue() != local.info) {
+    for (Object object : localNodes) {
+      LocalVariableNode node = (LocalVariableNode) object;
+      DebugLocalInfo nodeInfo = localVariables.get(localNodeEquivalence.wrap(node));
+      if (nodeInfo != local.info) {
         continue;
       }
       Type type = Type.getType(node.desc);
@@ -279,7 +338,8 @@
     Collection<LocalVariableNode> nodes = localVariableStartPoints.get(label);
     ArrayList<Local> locals = new ArrayList<>(nodes.size());
     for (LocalVariableNode node : nodes) {
-      locals.add(setLocalInfo(node.index, Type.getType(node.desc), localVariables.get(node)));
+      DebugLocalInfo info = localVariables.get(localNodeEquivalence.wrap(node));
+      locals.add(setLocalInfo(node.index, Type.getType(node.desc), info));
     }
     // Sort to ensure deterministic instruction ordering (correctness is unaffected).
     locals.sort(Comparator.comparingInt(local -> local.slot.register));
@@ -501,14 +561,15 @@
         }
       }
       // TODO(zerny): Precompute and sort the local ranges.
-      for (Entry<LocalVariableNode, DebugLocalInfo> entry : localVariables.entrySet()) {
-        LocalVariableNode node = entry.getKey();
+      for (Object object : localNodes) {
+        LocalVariableNode node = (LocalVariableNode) object;
         int startOffset = source.getOffset(node.start);
         int endOffset = source.getOffset(node.end);
         if (startOffset <= target && target < endOffset) {
           int register = getLocalRegister(node.index, Type.getType(node.desc));
           Local local = locals[register];
-          locals[register] = new Local(local.slot, entry.getValue());
+          DebugLocalInfo info = localVariables.get(localNodeEquivalence.wrap(node));
+          locals[register] = new Local(local.slot, info);
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/utils/VersionProperties.java b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
new file mode 100644
index 0000000..c2253de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/VersionProperties.java
@@ -0,0 +1,60 @@
+// 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.utils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * A class describing version properties.
+ */
+public class VersionProperties {
+  private static final int VERSION_CODE = 1;
+
+  private static final String VERSION_CODE_KEY = "version-file.version.code";
+  private static final String SHA_KEY = "version.sha";
+  private static final String RELEASER_KEY = "releaser";
+
+  private static final String RESOURCE_NAME = "r8-version.properties";
+
+  private String codeBase;
+  private String releaser;
+
+  public VersionProperties(ClassLoader loader)
+      throws IOException {
+    try (InputStream resourceStream = loader.getResourceAsStream(RESOURCE_NAME)) {
+      if (resourceStream == null) {
+        throw new FileNotFoundException(RESOURCE_NAME);
+      }
+      initWithInputStream(resourceStream);
+    }
+  }
+
+  private void initWithInputStream(InputStream is) throws IOException {
+    Properties prop = new Properties();
+    prop.load(is);
+
+    long versionFileVersion = Long.parseLong(prop.getProperty(VERSION_CODE_KEY));
+    assert versionFileVersion >= 1;
+
+    codeBase = prop.getProperty(SHA_KEY);
+    releaser = prop.getProperty(RELEASER_KEY);
+  }
+
+  public String getDescription() {
+    if (codeBase != null && !codeBase.trim().isEmpty()) {
+      return "build " + codeBase + (releaser != null ? " from " + releaser : "");
+    } else {
+      return "eng build" + (releaser != null ? " from " + releaser : "");
+    }
+  }
+
+  @Override
+  public String toString() {
+    return codeBase + " from " + releaser;
+  }
+}
diff --git a/tools/archive.py b/tools/archive.py
index 2ab1f1f..bb6c984 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -10,12 +10,14 @@
 import subprocess
 import sys
 import utils
+import shutil
+import zipfile
 
 ARCHIVE_BUCKET = 'r8-releases'
 
 def GetVersion():
-  r8_version = r8.run(['--version'], build = False).strip()
-  d8_version = d8.run(['--version'], build = False).strip()
+  r8_version = r8.run(['--version'], build = False).splitlines()[0].strip()
+  d8_version = d8.run(['--version'], build = False).splitlines()[0].strip()
   # The version printed is "D8 vVERSION_NUMBER" and "R8 vVERSION_NUMBER"
   # Sanity check that versions match.
   if d8_version.split()[1] != r8_version.split()[1]:
@@ -63,7 +65,7 @@
   if not 'BUILDBOT_BUILDERNAME' in os.environ:
     raise Exception('You are not a bot, don\'t archive builds')
   version = GetVersion()
-  is_master = IsMaster(version)
+  is_master = True #IsMaster(version)
   if is_master:
     # On master we use the git hash to archive with
     print 'On master, using git hash for archiving'
@@ -72,12 +74,23 @@
   # Ensure all archived artifacts has been built before archiving.
   gradle.RunGradle([utils.D8, utils.R8, utils.COMPATDX, utils.COMPATPROGUARD])
 
-  for jar in [utils.D8_JAR, utils.R8_JAR, utils.COMPATDX_JAR, utils.COMPATPROGUARD_JAR]:
-    file_name = os.path.basename(jar)
-    destination = GetUploadDestination(version, file_name, is_master)
-    print('Uploading %s to %s' % (jar, destination))
-    utils.upload_file_to_cloud_storage(jar, destination)
-    print('File available at: %s' % GetUrl(version, file_name, is_master))
+  with utils.TempDir() as temp:
+    version_file = os.path.join(temp, 'r8-version.properties')
+    with open(version_file,'w') as version_writer:
+      version_writer.write('version.sha=' + GetGitHash() + '\n')
+      version_writer.write('releaser=' + os.environ.get('BUILDBOT_BUILDERNAME') + '\n')
+      version_writer.write('version-file.version.code=1\n')
+
+    for jar in [utils.D8_JAR, utils.R8_JAR, utils.COMPATDX_JAR, utils.COMPATPROGUARD_JAR]:
+      file_name = os.path.basename(jar)
+      tagged_jar = os.path.join(temp, file_name)
+      shutil.copyfile(jar, tagged_jar)
+      with zipfile.ZipFile(tagged_jar, 'a') as zip:
+        zip.write(version_file, os.path.basename(version_file))
+      destination = GetUploadDestination(version, file_name, is_master)
+      print('Uploading %s to %s' % (tagged_jar, destination))
+      utils.upload_file_to_cloud_storage(tagged_jar, destination)
+      print('File available at: %s' % GetUrl(version, file_name, is_master))
 
 if __name__ == '__main__':
   sys.exit(Main())