Merge "Read input classes in parallel"
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index aa32b12..cca3520 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -214,10 +214,16 @@
         ClassKind classKind, Queue<T> classes) throws IOException, ExecutionException {
       JarClassFileReader reader = new JarClassFileReader(
           application, classKind.bridgeConsumer(classes::add));
+      // Read classes in parallel.
       for (Resource input : classSources) {
-        try (InputStream is = input.getStream()) {
-          reader.read(DEFAULT_DEX_FILENAME, classKind, is);
-        }
+        futures.add(executorService.submit(() -> {
+          try (InputStream is = input.getStream()) {
+            reader.read(DEFAULT_DEX_FILENAME, classKind, is);
+          }
+          // No other way to have a void callable, but we want the IOException from the previous
+          // line to be wrapped into an ExecutionException.
+          return null;
+        }));
       }
     }
 
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 13a4d9d..80dea00 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -24,17 +24,19 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
 public class DexItemFactory {
 
-  private final Map<DexString, DexString> strings = new HashMap<>();
-  private final Map<DexString, DexType> types = new HashMap<>();
-  private final Map<DexField, DexField> fields = new HashMap<>();
-  private final Map<DexProto, DexProto> protos = new HashMap<>();
-  private final Map<DexMethod, DexMethod> methods = new HashMap<>();
-  private final Map<DexCallSite, DexCallSite> callSites = new HashMap<>();
-  private final Map<DexMethodHandle, DexMethodHandle> methodHandles = new HashMap<>();
+  private final ConcurrentHashMap<DexString, DexString> strings = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexString, DexType> types = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexField, DexField> fields = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexProto, DexProto> protos = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexMethod, DexMethod> methods = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexCallSite, DexCallSite> callSites = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<DexMethodHandle, DexMethodHandle> methodHandles =
+      new ConcurrentHashMap<>();
 
   // DexDebugEvent Canonicalization.
   private final Int2ObjectMap<AdvanceLine> advanceLines = new Int2ObjectOpenHashMap<>();
@@ -280,7 +282,7 @@
     }
   }
 
-  synchronized private static <T extends DexItem> T canonicalize(Map<T, T> map, T item) {
+  private static <T extends DexItem> T canonicalize(ConcurrentHashMap<T, T> map, T item) {
     assert item != null;
     assert !internalSentinels.contains(item);
     T previous = map.putIfAbsent(item, item);
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
index 1812fe1..671698b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidClassNames.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
+import static junit.framework.Assert.assertNull;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.fail;
 
@@ -10,6 +11,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,13 +36,17 @@
     String artResult = null;
     try {
       artResult = runOnArt(builder, main);
-    } catch (CompilationError t) {
-      // Ignore.
+      fail();
+    } catch (ExecutionException t) {
+      if (!(t.getCause() instanceof CompilationError)) {
+        t.printStackTrace(System.out);
+        fail("Invalid dex class names should be compilation errors.");
+      }
     } catch (Throwable t) {
       t.printStackTrace(System.out);
       fail("Invalid dex class names should be compilation errors.");
     }
-    assert artResult == null : "Invalid dex class names should be rejected.";
+    assertNull("Invalid dex class names should be rejected.", artResult);
   }
 
   @Parameters
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
index 791ecca..87d350c 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidFieldNames.java
@@ -3,11 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
+import static junit.framework.Assert.assertNull;
 import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.fail;
 
 import com.android.tools.r8.errors.CompilationError;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -32,10 +35,14 @@
     String artResult = null;
     try {
       artResult = runOnArt(builder, main);
-    } catch (CompilationError t) {
-      // Ignore.
+      fail();
+    } catch (ExecutionException t) {
+      if (!(t.getCause() instanceof CompilationError)) {
+        t.printStackTrace(System.out);
+        fail("Invalid dex field names should be compilation errors.");
+      }
     }
-    assert artResult == null : "Invalid dex class names should be rejected.";
+    assertNull("Invalid dex field names should be rejected.", artResult);
   }
 
   @Parameters
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
index f8b7615..e1ace71 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidMethodNames.java
@@ -3,13 +3,16 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
+import static junit.framework.Assert.assertNull;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.fail;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.errors.CompilationError;
 import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,13 +37,17 @@
     String artResult = null;
     try {
       artResult = runOnArt(builder, main);
-    } catch (CompilationError t) {
-      // Ignore.
+      fail();
+    } catch (ExecutionException t) {
+      if (!(t.getCause() instanceof CompilationError)) {
+        t.printStackTrace(System.out);
+        fail("Invalid dex method names should be compilation errors.");
+      }
     } catch (Throwable t) {
       t.printStackTrace(System.out);
-      fail("Invalid dex class names should be compilation errors.");
+      fail("Invalid dex method names should be compilation errors.");
     }
-    assert artResult == null : "Invalid dex class names should be rejected.";
+    assertNull("Invalid dex method names should be rejected.", artResult);
   }
 
   @Parameters