Merge "Only remove visibility bridges"
diff --git a/build.gradle b/build.gradle
index 0663b8a..45eade1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -151,6 +151,7 @@
         "gmscore/v8.tar.gz",
         "gmscore/gmscore_v9.tar.gz",
         "gmscore/gmscore_v10.tar.gz",
+        "photos/2017-06-06.tar.gz",
         "youtube/youtube.android_11.47.tar.gz",
         "youtube/youtube.android_12.10.tar.gz",
         "youtube/youtube.android_12.17.tar.gz",
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index c7760bb..eaabc99 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -1,30 +1,97 @@
 // 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;
 
 import com.google.common.io.Closer;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Path;
 import java.util.Set;
 
 /** Represents application resources. */
-public interface Resource {
-
-  /** Application resource kind. */
-  enum Kind {
-    PROGRAM, CLASSPATH, LIBRARY
+public abstract class Resource {
+  /** Kind of the resource describing the resource content. */
+  public enum Kind {
+    DEX, CLASSFILE
   }
 
-  /** Get the kind of the resource. */
-  Kind getKind();
+  private Resource(Kind kind) {
+    this.kind = kind;
+  }
+
+  /** Kind of the resource. */
+  public final Kind kind;
+
+  /** Create an application resource for a given file. */
+  public static Resource fromFile(Kind kind, Path file) {
+    return new FileResource(kind, file);
+  }
+
+  /** Create an application resource for a given content. */
+  public static Resource fromBytes(Kind kind, byte[] bytes) {
+    return fromBytes(kind, bytes, null);
+  }
+
+  /** Create an application resource for a given content and type descriptor. */
+  public static Resource fromBytes(Kind kind, byte[] bytes, Set<String> typeDescriptors) {
+    return new ByteResource(kind, bytes, typeDescriptors);
+  }
 
   /**
    * Returns the set of class descriptors for classes represented
    * by the resource if known, or `null' otherwise.
    */
-  Set<String> getClassDescriptors();
+  public abstract Set<String> getClassDescriptors();
 
   /** Get the resource as a stream. */
-  InputStream getStream(Closer closer) throws IOException;
+  public abstract InputStream getStream(Closer closer) throws IOException;
+
+  /** File based application resource. */
+  private static class FileResource extends Resource {
+    final Path file;
+
+    FileResource(Kind kind, Path file) {
+      super(kind);
+      assert file != null;
+      this.file = file;
+    }
+
+    @Override
+    public Set<String> getClassDescriptors() {
+      return null;
+    }
+
+    @Override
+    public InputStream getStream(Closer closer) throws IOException {
+      return closer.register(new FileInputStream(file.toFile()));
+    }
+  }
+
+  /** Byte content based application resource. */
+  private static class ByteResource extends Resource {
+    final Set<String> classDescriptors;
+    final byte[] bytes;
+
+    ByteResource(Kind kind, byte[] bytes, Set<String> classDescriptors) {
+      super(kind);
+      assert bytes != null;
+      this.classDescriptors = classDescriptors;
+      this.bytes = bytes;
+    }
+
+    @Override
+    public Set<String> getClassDescriptors() {
+      return classDescriptors;
+    }
+
+    @Override
+    public InputStream getStream(Closer closer) throws IOException {
+      // Note: closing a byte-array input stream is a no-op.
+      return new ByteArrayInputStream(bytes);
+    }
+  }
 }
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 35f9bfe..af6bebe 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.ResourceProvider;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
@@ -22,7 +23,6 @@
 import com.android.tools.r8.naming.ProguardMapReader;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalResource;
 import com.android.tools.r8.utils.LazyClassCollection;
 import com.android.tools.r8.utils.MainDexList;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -32,6 +32,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -90,67 +91,68 @@
     JarApplicationReader application = new JarApplicationReader(options);
     JarClassFileReader reader = new JarClassFileReader(
         application, builder::addClassIgnoringLibraryDuplicates);
-    for (InternalResource input : inputApp.getClassProgramResources()) {
-      reader.read(DEFAULT_DEX_FILENAME, Resource.Kind.PROGRAM, input.getStream(closer));
+    for (Resource input : inputApp.getClassProgramResources()) {
+      reader.read(DEFAULT_DEX_FILENAME, ClassKind.PROGRAM, input.getStream(closer));
     }
-    for (InternalResource input : inputApp.getClassClasspathResources()) {
-      if (options.lazyClasspathLoading && input.getSingleClassDescriptorOrNull() != null) {
-        addLazyLoader(application, builder, input);
+    for (Resource input : inputApp.getClassClasspathResources()) {
+      if (options.lazyClasspathLoading && getResourceClassDescriptorOrNull(input) != null) {
+        addLazyLoader(application, ClassKind.CLASSPATH, builder, input);
       } else {
-        reader.read(DEFAULT_DEX_FILENAME, Resource.Kind.CLASSPATH, input.getStream(closer));
+        reader.read(DEFAULT_DEX_FILENAME, ClassKind.CLASSPATH, input.getStream(closer));
       }
     }
-    for (InternalResource input : inputApp.getClassLibraryResources()) {
-      if (options.lazyLibraryLoading && input.getSingleClassDescriptorOrNull() != null) {
-        addLazyLoader(application, builder, input);
+    for (Resource input : inputApp.getClassLibraryResources()) {
+      if (options.lazyLibraryLoading && getResourceClassDescriptorOrNull(input) != null) {
+        addLazyLoader(application, ClassKind.LIBRARY, builder, input);
       } else {
-        reader.read(DEFAULT_DEX_FILENAME, Resource.Kind.LIBRARY, input.getStream(closer));
+        reader.read(DEFAULT_DEX_FILENAME, ClassKind.LIBRARY, input.getStream(closer));
       }
     }
   }
 
   private void initializeLazyClassCollection(DexApplication.Builder builder) {
-    List<ResourceProvider> providers = inputApp.getLazyResourceProviders();
-    if (!providers.isEmpty()) {
-      builder.setLazyClassCollection(
-          new LazyClassCollection(new JarApplicationReader(options), providers));
+    List<ResourceProvider> classpathProviders = inputApp.getClasspathResourceProviders();
+    List<ResourceProvider> libraryProviders = inputApp.getLibraryResourceProviders();
+    if (!classpathProviders.isEmpty() || !libraryProviders.isEmpty()) {
+      builder.setLazyClassCollection(new LazyClassCollection(
+          new JarApplicationReader(options), classpathProviders, libraryProviders));
     }
   }
 
   private void addLazyLoader(JarApplicationReader application,
-      DexApplication.Builder builder, InternalResource resource) {
+      ClassKind classKind, DexApplication.Builder builder, Resource resource) {
     // Generate expected DEX type.
-    String classDescriptor = resource.getSingleClassDescriptorOrNull();
+    String classDescriptor = getResourceClassDescriptorOrNull(resource);
     assert classDescriptor != null;
     DexType type = options.itemFactory.createType(classDescriptor);
-    LazyClassFileLoader newLoader = new LazyClassFileLoader(type, resource, application);
+    LazyClassFileLoader newLoader = new LazyClassFileLoader(type, resource, classKind, application);
     builder.addClassPromise(newLoader, true);
   }
 
   private void readDexSources(DexApplication.Builder builder, ExecutorService executorService,
       List<Future<?>> futures, Closer closer)
       throws IOException, ExecutionException {
-    List<InternalResource> dexProgramSources = inputApp.getDexProgramResources();
-    List<InternalResource> dexClasspathSources = inputApp.getDexClasspathResources();
-    List<InternalResource> dexLibrarySources = inputApp.getDexLibraryResources();
+    List<Resource> dexProgramSources = inputApp.getDexProgramResources();
+    List<Resource> dexClasspathSources = inputApp.getDexClasspathResources();
+    List<Resource> dexLibrarySources = inputApp.getDexLibraryResources();
     int numberOfFiles = dexProgramSources.size()
         + dexLibrarySources.size() + dexClasspathSources.size();
     if (numberOfFiles > 0) {
       List<DexFileReader> fileReaders = new ArrayList<>(numberOfFiles);
       int computedMinApiLevel = options.minApiLevel;
-      for (InternalResource input : dexProgramSources) {
+      for (Resource input : dexProgramSources) {
         DexFile file = new DexFile(input.getStream(closer));
         computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
-        fileReaders.add(new DexFileReader(file, Resource.Kind.PROGRAM, itemFactory));
+        fileReaders.add(new DexFileReader(file, ClassKind.PROGRAM, itemFactory));
       }
-      for (InternalResource input : dexClasspathSources) {
+      for (Resource input : dexClasspathSources) {
         DexFile file = new DexFile(input.getStream(closer));
-        fileReaders.add(new DexFileReader(file, Resource.Kind.CLASSPATH, itemFactory));
+        fileReaders.add(new DexFileReader(file, ClassKind.CLASSPATH, itemFactory));
       }
-      for (InternalResource input : dexLibrarySources) {
+      for (Resource input : dexLibrarySources) {
         DexFile file = new DexFile(input.getStream(closer));
         computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
-        fileReaders.add(new DexFileReader(file, Resource.Kind.LIBRARY, itemFactory));
+        fileReaders.add(new DexFileReader(file, ClassKind.LIBRARY, itemFactory));
       }
       options.minApiLevel = computedMinApiLevel;
       for (DexFileReader reader : fileReaders) {
@@ -230,4 +232,9 @@
     }
   }
 
+  private static String getResourceClassDescriptorOrNull(Resource resource) {
+    Set<String> descriptors = resource.getClassDescriptors();
+    return (descriptors == null) || (descriptors.size() != 1)
+        ? null : descriptors.iterator().next();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 02b0ce8..6aacd55 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.InstructionFactory;
+import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexAnnotation;
@@ -68,12 +69,12 @@
   private DexFile file;
   private final Segment[] segments;
   private int[] stringIDs;
-  private final Resource.Kind fileKind;
+  private final ClassKind classKind;
 
   public static Segment[] parseMapFrom(Path file) throws IOException {
     DexFileReader reader =
         new DexFileReader(
-            new DexFile(file.toString()), Resource.Kind.PROGRAM, new DexItemFactory());
+            new DexFile(file.toString()), ClassKind.PROGRAM, new DexItemFactory());
     return reader.parseMap();
   }
 
@@ -98,14 +99,13 @@
   // Factory to canonicalize certain dexitems.
   private final DexItemFactory dexItemFactory;
 
-  public DexFileReader(
-      DexFile file, Resource.Kind fileKind, DexItemFactory dexItemFactory) {
+  public DexFileReader(DexFile file, ClassKind classKind, DexItemFactory dexItemFactory) {
     this.file = file;
     this.dexItemFactory = dexItemFactory;
     file.setByteOrder();
     segments = parseMap();
     parseStringIDs();
-    this.fileKind = fileKind;
+    this.classKind = classKind;
   }
 
   public OffsetToObjectMapping getIndexedItemsMap() {
@@ -113,7 +113,7 @@
   }
 
   void addCodeItemsTo() {
-    if (fileKind == Resource.Kind.LIBRARY) {
+    if (classKind == ClassKind.LIBRARY) {
       // Ignore contents of library files.
       return;
     }
@@ -644,17 +644,17 @@
                 directMethodsSize,
                 annotationsDirectory.methods,
                 annotationsDirectory.parameters,
-                fileKind != Resource.Kind.PROGRAM);
+                classKind != ClassKind.PROGRAM);
         virtualMethods =
             readMethods(
                 virtualMethodsSize,
                 annotationsDirectory.methods,
                 annotationsDirectory.parameters,
-                fileKind != Resource.Kind.PROGRAM);
+                classKind != ClassKind.PROGRAM);
       }
-      clazz = DexClass.factoryForResourceKind(fileKind).create(
+      clazz = classKind.create(
           type,
-          DexClass.Origin.Dex,
+          Resource.Kind.DEX, 
           flags,
           superclass,
           typeListAt(interfacesOffsets[i]),
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index ad5420d..07fce3a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -63,7 +63,6 @@
   private static final int MAX_PREFILL_ENTRIES = MAX_ENTRIES - 5000;
 
   private final int id;
-  private final Set<String> classDescriptors = new HashSet<>();
   private final VirtualFileIndexedItemCollection indexedItems;
   private final IndexedItemTransaction transaction;
 
@@ -74,6 +73,11 @@
   }
 
   public Set<String> getClassDescriptors() {
+    Set<String> classDescriptors = new HashSet<>();
+    for (DexProgramClass clazz : indexedItems.classes) {
+      boolean added = classDescriptors.add(clazz.type.descriptor.toString());
+      assert added;
+    }
     return classDescriptors;
   }
 
@@ -289,7 +293,6 @@
 
   private void addClass(DexProgramClass clazz) {
     transaction.addClassAndDependencies(clazz);
-    classDescriptors.add(clazz.type.descriptor.toString());
   }
 
   private static boolean isFull(int numberOfMethods, int numberOfFields, int maximum) {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
new file mode 100644
index 0000000..6f53a40
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -0,0 +1,33 @@
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.Resource;
+
+/** Kind of the application class. Can be program, classpath or library. */
+public enum ClassKind {
+  PROGRAM(DexProgramClass::new),
+  CLASSPATH(DexClasspathClass::new),
+  LIBRARY(DexLibraryClass::new);
+
+  private interface Factory {
+    DexClass create(DexType type, Resource.Kind origin, DexAccessFlags accessFlags,
+        DexType superType,
+        DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+        DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+        DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods);
+  }
+
+  private final Factory factory;
+
+  ClassKind(Factory factory) {
+    this.factory = factory;
+  }
+
+  public DexClass create(
+      DexType type, Resource.Kind origin, DexAccessFlags accessFlags, DexType superType,
+      DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+      DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
+    return factory.create(type, origin, accessFlags, superType, interfaces, sourceFile,
+        annotations, staticFields, instanceFields, directMethods, virtualMethods);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index bcc845e..acbc205 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -6,6 +6,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.ir.desugar.LambdaRewriter;
 import com.android.tools.r8.logging.Log;
@@ -387,8 +388,8 @@
 
   private static boolean allowProgramClassConflict(DexClassPromise a, DexClassPromise b) {
     // Currently only allow collapsing synthetic lambda classes.
-    return a.getOrigin() == DexClass.Origin.Dex
-        && b.getOrigin() == DexClass.Origin.Dex
+    return a.getOrigin() == Resource.Kind.DEX
+        && b.getOrigin() == Resource.Kind.DEX
         && a.get().accessFlags.isSynthetic()
         && b.get().accessFlags.isSynthetic()
         && LambdaRewriter.hasLambdaClassPrefix(a.getType())
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 90828b7..f684309 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -7,27 +7,14 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.utils.InternalResource;
 import com.google.common.base.MoreObjects;
 
 public abstract class DexClass extends DexItem implements DexClassPromise {
 
-  public interface Factory {
-
-    DexClass create(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
-        DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
-        DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
-        DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods);
-  }
-
-  public enum Origin {
-    Dex, ClassFile, Synthetic
-  }
-
   private static final DexEncodedMethod[] NO_METHODS = {};
   private static final DexEncodedField[] NO_FIELDS = {};
 
-  public final Origin origin;
+  public final Resource.Kind origin;
   public final DexType type;
   public final DexAccessFlags accessFlags;
   public DexType superType;
@@ -43,7 +30,7 @@
       DexString sourceFile, DexTypeList interfaces, DexAccessFlags accessFlags, DexType superType,
       DexType type, DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods,
-      DexAnnotationSet annotations, Origin origin) {
+      DexAnnotationSet annotations, Resource.Kind origin) {
     this.origin = origin;
     this.sourceFile = sourceFile;
     this.interfaces = interfaces;
@@ -168,7 +155,7 @@
   }
 
   @Override
-  public Origin getOrigin() {
+  public Resource.Kind getOrigin() {
     return this.origin;
   }
 
@@ -182,19 +169,6 @@
     return type;
   }
 
-  /** Get a class factory for a particular resource kind */
-  public static Factory factoryForResourceKind(Resource.Kind kind) {
-    switch (kind) {
-      case PROGRAM:
-        return DexProgramClass::new;
-      case CLASSPATH:
-        return DexClasspathClass::new;
-      case LIBRARY:
-        return DexLibraryClass::new;
-    }
-    throw new Unreachable();
-  }
-
   public boolean hasClassInitializer() {
     return getClassInitializer() != null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
index 243eadf..c0bdf20 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.Resource;
+
 /**
  * Provides a way for delayed DexClass discovery.
  *
@@ -15,7 +17,7 @@
 public interface DexClassPromise {
   DexType getType();
 
-  DexClass.Origin getOrigin();
+  Resource.Kind getOrigin();
 
   boolean isProgramClass();
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 0ec8cf7..dd5efbb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -3,13 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 
 public class DexClasspathClass extends DexClass {
 
-  public DexClasspathClass(DexType type, Origin origin, DexAccessFlags accessFlags,
+  public DexClasspathClass(DexType type, Resource.Kind origin, DexAccessFlags accessFlags,
       DexType superType, DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
       DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index c4ed702..c1b9205 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -3,14 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 
 public class DexLibraryClass extends DexClass {
 
-  public DexLibraryClass(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
-      DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+  public DexLibraryClass(DexType type, Resource.Kind origin, DexAccessFlags accessFlags,
+      DexType superType, DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
       DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
     super(sourceFile, interfaces, accessFlags, superType, type,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 6713083..094a039 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import java.util.Arrays;
@@ -12,7 +13,7 @@
   private DexEncodedArray staticValues;
 
   public DexProgramClass(DexType type,
-      Origin origin,
+      Resource.Kind origin,
       DexAccessFlags accessFlags,
       DexType superType,
       DexTypeList interfaces,
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 3cc3ac4..c738e17 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -58,10 +58,10 @@
     this.classConsumer = classConsumer;
   }
 
-  public void read(String file, Resource.Kind kind, InputStream input) throws IOException {
+  public void read(String file, ClassKind classKind, InputStream input) throws IOException {
     ClassReader reader = new ClassReader(input);
     reader.accept(new CreateDexClassVisitor(
-        file, kind, reader.b, application, classConsumer), SKIP_FRAMES);
+        file, classKind, reader.b, application, classConsumer), SKIP_FRAMES);
   }
 
   private static DexAccessFlags createAccessFlags(int access) {
@@ -70,11 +70,11 @@
   }
 
   private static AnnotationVisitor createAnnotationVisitor(String desc, boolean visible,
-      AnnotationVisitor annotationVisitor, List<DexAnnotation> annotations,
+      List<DexAnnotation> annotations,
       JarApplicationReader application) {
     assert annotations != null;
     int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
-    return new CreateAnnotationVisitor(annotationVisitor, application, (names, values) ->
+    return new CreateAnnotationVisitor(application, (names, values) ->
         annotations.add(new DexAnnotation(visiblity,
             createEncodedAnnotation(desc, names, values, application))));
   }
@@ -92,7 +92,7 @@
 
   private static class CreateDexClassVisitor extends ClassVisitor {
     private final String file;
-    private final Resource.Kind kind;
+    private final ClassKind classKind;
     private final JarApplicationReader application;
     private final Consumer<DexClass> classConsumer;
     private final ReparseContext context = new ReparseContext();
@@ -115,13 +115,13 @@
 
     public CreateDexClassVisitor(
         String file,
-        Resource.Kind kind,
+        ClassKind classKind,
         byte[] classCache,
         JarApplicationReader application,
         Consumer<DexClass> classConsumer) {
       super(ASM5);
       this.file = file;
-      this.kind = kind;
+      this.classKind = classKind;
       this.classConsumer = classConsumer;
       this.context.classCache = classCache;
       this.application = application;
@@ -210,7 +210,7 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible, null, getAnnotations(), application);
+      return createAnnotationVisitor(desc, visible, getAnnotations(), application);
     }
 
     @Override
@@ -247,9 +247,9 @@
         addAnnotation(DexAnnotation.createAnnotationDefaultAnnotation(
             type, defaultAnnotations, application.getFactory()));
       }
-      DexClass clazz = DexClass.factoryForResourceKind(kind).create(
+      DexClass clazz = classKind.create(
           type,
-          DexClass.Origin.ClassFile,
+          Resource.Kind.CLASSFILE, 
           accessFlags,
           superType,
           interfaces,
@@ -259,7 +259,7 @@
           instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
           directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
           virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]));
-      if (kind == Resource.Kind.PROGRAM) {
+      if (classKind == ClassKind.PROGRAM) {
         context.owner = clazz.asProgramClass();
       }
       classConsumer.accept(clazz);
@@ -314,7 +314,7 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible, null, getAnnotations(), parent.application);
+      return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
     }
 
     @Override
@@ -424,15 +424,12 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
-      return createAnnotationVisitor(desc, visible,
-          mv == null ? null : mv.visitAnnotation(desc, visible),
-          getAnnotations(), parent.application);
+      return createAnnotationVisitor(desc, visible, getAnnotations(), parent.application);
     }
 
     @Override
     public AnnotationVisitor visitAnnotationDefault() {
-      return new CreateAnnotationVisitor(mv == null ? null : mv.visitAnnotationDefault(),
-          parent.application, (names, elements) -> {
+      return new CreateAnnotationVisitor(parent.application, (names, elements) -> {
         assert elements.size() == 1;
         defaultAnnotation = elements.get(0);
       });
@@ -455,8 +452,12 @@
       // never see this non-existing annotation descriptor. ASM uses the same check to make
       // sure to undo their workaround for the javac bug in their MethodWriter.
       if (desc.equals("Ljava/lang/Synthetic;")) {
-        assert parameterAnnotations == null;
-        fakeParameterAnnotations++;
+        // We can iterate through all the parameters twice. Once for visible and once for
+        // invisible parameter annotations. We only record the number of fake parameter
+        // annotations once.
+        if (parameterAnnotations == null) {
+          fakeParameterAnnotations++;
+        }
         return null;
       }
       if (parameterAnnotations == null) {
@@ -466,8 +467,8 @@
           parameterAnnotations.add(new ArrayList<>());
         }
       }
+      assert mv == null;
       return createAnnotationVisitor(desc, visible,
-          mv == null ? null : mv.visitParameterAnnotation(parameter, desc, visible),
           parameterAnnotations.get(parameter - fakeParameterAnnotations), parent.application);
     }
 
@@ -511,7 +512,7 @@
       Code code = null;
       if (!flags.isAbstract()
           && !flags.isNative()
-          && parent.kind == Resource.Kind.PROGRAM) {
+          && parent.classKind == ClassKind.PROGRAM) {
         code = new JarCode(method, parent.context, parent.application);
       }
       DexAnnotationSetRefList parameterAnnotationSets;
@@ -584,10 +585,9 @@
     private List<DexString> names = null;
     private final List<DexValue> values = new ArrayList<>();
 
-    public CreateAnnotationVisitor(AnnotationVisitor annotationVisitor,
-        JarApplicationReader application,
-        BiConsumer<List<DexString>, List<DexValue>> onVisitEnd) {
-      super(ASM5, annotationVisitor);
+    public CreateAnnotationVisitor(
+        JarApplicationReader application, BiConsumer<List<DexString>, List<DexValue>> onVisitEnd) {
+      super(ASM5);
       this.application = application;
       this.onVisitEnd = onVisitEnd;
     }
@@ -605,14 +605,14 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String name, String desc) {
-      return new CreateAnnotationVisitor(av, application, (names, values) ->
+      return new CreateAnnotationVisitor(application, (names, values) ->
           addElement(name, new DexValueAnnotation(
               createEncodedAnnotation(desc, names, values, application))));
     }
 
     @Override
     public AnnotationVisitor visitArray(String name) {
-      return new CreateAnnotationVisitor(av, application, (names, values) -> {
+      return new CreateAnnotationVisitor(application, (names, values) -> {
         assert names == null;
         addElement(name, new DexValueArray(values.toArray(new DexValue[values.size()])));
       });
diff --git a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
index ce40d19..e5792cd 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
@@ -9,14 +9,15 @@
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.utils.InternalResource;
 import com.google.common.io.Closer;
 import java.io.IOException;
 
 // Lazily loads a class file represented by resource.
 public final class LazyClassFileLoader implements DexClassPromise {
   // Resource representing file definition.
-  private final InternalResource resource;
+  private final Resource resource;
+  // Class kind to be created.
+  private final ClassKind classKind;
 
   // Application reader to be used. Note that the reader may be reused in
   // many loaders and may be used concurrently, it is considered to be
@@ -32,10 +33,13 @@
   // field is only accessed in context synchronized on `this`.
   private DexClass loadedClass = null;
 
-  public LazyClassFileLoader(DexType type, InternalResource resource, JarApplicationReader reader) {
+  public LazyClassFileLoader(DexType type,
+      Resource resource, ClassKind classKind, JarApplicationReader reader) {
     this.resource = resource;
     this.reader = reader;
     this.type = type;
+    this.classKind = classKind;
+    assert classKind != ClassKind.PROGRAM;
   }
 
   // Callback method for JarClassFileReader, is always called in synchronized context.
@@ -51,23 +55,23 @@
   }
 
   @Override
-  public DexClass.Origin getOrigin() {
-    return DexClass.Origin.ClassFile;
+  public Resource.Kind getOrigin() {
+    return Resource.Kind.CLASSFILE;
   }
 
   @Override
   public boolean isProgramClass() {
-    return resource.getKind() == Resource.Kind.PROGRAM;
+    return false;
   }
 
   @Override
   public boolean isClasspathClass() {
-    return resource.getKind() == Resource.Kind.CLASSPATH;
+    return classKind == ClassKind.CLASSPATH;
   }
 
   @Override
   public boolean isLibraryClass() {
-    return resource.getKind() == Resource.Kind.LIBRARY;
+    return classKind == ClassKind.LIBRARY;
   }
 
   // Loads the class from the resource. Synchronized on `this` to avoid
@@ -81,7 +85,7 @@
 
     try (Closer closer = Closer.create()) {
       JarClassFileReader reader = new JarClassFileReader(this.reader, this::addClass);
-      reader.read(DEFAULT_DEX_FILENAME, resource.getKind(), resource.getStream(closer));
+      reader.read(DEFAULT_DEX_FILENAME, classKind, resource.getStream(closer));
     } catch (IOException e) {
       throw new CompilationError("Failed to load class: " + type.toSourceString(), e);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 215cfba..01be50d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -4,12 +4,12 @@
 
 package com.android.tools.r8.ir.desugar;
 
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassPromise;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -217,7 +217,7 @@
 
   private static boolean shouldProcess(
       DexProgramClass clazz, Flavor flavour, boolean mustBeInterface) {
-    return (clazz.getOrigin() != DexClass.Origin.Dex || flavour == Flavor.IncludeAllResources)
+    return (clazz.getOrigin() != Resource.Kind.DEX || flavour == Flavor.IncludeAllResources)
         && clazz.isInterface() == mustBeInterface;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 66cd7d9..d1b1577 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -128,7 +127,7 @@
     DexType companionClassType = rewriter.getCompanionClassType(iface.type);
     DexProgramClass companionClass = new DexProgramClass(
         companionClassType,
-        DexClass.Origin.Synthetic,
+        null,
         companionClassFlags,
         rewriter.factory.objectType,
         DexTypeList.empty(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index db28115..37bfdaa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexAnnotationSetRefList;
-import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassPromise;
 import com.android.tools.r8.graph.DexCode;
@@ -114,7 +113,7 @@
   final DexProgramClass synthesizeLambdaClass() {
     return new DexProgramClass(
         type,
-        DexClass.Origin.Synthetic,
+        null,
         new DexAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC),
         rewriter.factory.objectType,
         buildInterfaces(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 80a5975..42b5220 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -1028,7 +1028,7 @@
     DexAccessFlags accessFlags = new DexAccessFlags(Constants.ACC_PUBLIC);
     DexProgramClass clazz = new DexProgramClass(
         type,
-        DexClass.Origin.Synthetic,
+        null,
         accessFlags,
         superType,
         interfaces,
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 a981990..3a96ac2 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
@@ -870,7 +870,7 @@
     assert leftReg != NO_REGISTER && rightReg != NO_REGISTER;
     // The dalvik bug is actually only for overlap with the second operand, For now we
     // make sure that there is no overlap with either operand.
-    if ((leftReg + 1) == register|| (rightReg + 1) == register) {
+    if ((leftReg + 1) == register || (rightReg + 1) == register) {
       return true;
     }
     return false;
@@ -977,14 +977,8 @@
 
     // Get the register (pair) that is free the longest. That is the register with the largest
     // free position.
-    int candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
-    if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
-      while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
-        // Make the overlapping register unavailable for allocation and try again.
-        freePositions.set(candidate, 0);
-        candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
-      }
-    }
+    int candidate = getLargestValidCandidate(
+        unhandledInterval, registerConstraint, needsRegisterPair, freePositions);
     int largestFreePosition = freePositions.get(candidate);
     if (needsRegisterPair) {
       largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
@@ -1162,6 +1156,19 @@
     return candidate;
   }
 
+  private int getLargestValidCandidate(LiveIntervals unhandledInterval, int registerConstraint,
+      boolean needsRegisterPair, RegisterPositions freePositions) {
+    int candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+    if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
+      while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
+        // Make the overlapping register unavailable for allocation and try again.
+        freePositions.set(candidate, 0);
+        candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+      }
+    }
+    return candidate;
+  }
+
   private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
     int registerConstraint = unhandledInterval.getRegisterLimit();
     if (registerConstraint < Constants.U16BIT_MAX) {
@@ -1225,9 +1232,9 @@
         inactive, unhandledInterval, registerConstraint, usePositions, blockedPositions);
 
     // Get the register (pair) that has the highest use position.
-    assert unhandledInterval.requiredRegisters() <= 2;
     boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
-    int candidate = getLargestCandidate(registerConstraint, usePositions, needsRegisterPair);
+    int candidate = getLargestValidCandidate(
+        unhandledInterval, registerConstraint, needsRegisterPair, usePositions);
     int largestUsePosition = usePositions.get(candidate);
     int blockedPosition = blockedPositions.get(candidate);
     if (needsRegisterPair) {
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 2b78295..453a250 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.ResourceProvider;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.ClassKind;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
@@ -50,26 +52,32 @@
   public static final String DEFAULT_PROGUARD_SEEDS_FILE = "proguard.seeds";
   public static final String DEFAULT_PACKAGE_DISTRIBUTION_FILE = "package.map";
 
-  private final ImmutableList<InternalResource> dexSources;
-  private final ImmutableList<InternalResource> classSources;
-  private final ImmutableList<ResourceProvider> resourceProviders;
-  private final InternalResource proguardMap;
-  private final InternalResource proguardSeeds;
-  private final InternalResource packageDistribution;
-  private final InternalResource mainDexList;
+  private final ImmutableList<Resource> programResources;
+  private final ImmutableList<Resource> classpathResources;
+  private final ImmutableList<Resource> libraryResources;
+  private final ImmutableList<ResourceProvider> classpathResourceProviders;
+  private final ImmutableList<ResourceProvider> libraryResourceProviders;
+  private final Resource proguardMap;
+  private final Resource proguardSeeds;
+  private final Resource packageDistribution;
+  private final Resource mainDexList;
 
   // See factory methods and AndroidApp.Builder below.
   private AndroidApp(
-      ImmutableList<InternalResource> dexSources,
-      ImmutableList<InternalResource> classSources,
-      ImmutableList<ResourceProvider> resourceProviders,
-      InternalResource proguardMap,
-      InternalResource proguardSeeds,
-      InternalResource packageDistribution,
-      InternalResource mainDexList) {
-    this.dexSources = dexSources;
-    this.classSources = classSources;
-    this.resourceProviders = resourceProviders;
+      ImmutableList<Resource> programResources,
+      ImmutableList<Resource> classpathResources,
+      ImmutableList<Resource> libraryResources,
+      ImmutableList<ResourceProvider> classpathResourceProviders,
+      ImmutableList<ResourceProvider> libraryResourceProviders,
+      Resource proguardMap,
+      Resource proguardSeeds,
+      Resource packageDistribution,
+      Resource mainDexList) {
+    this.programResources = programResources;
+    this.classpathResources = classpathResources;
+    this.libraryResources = libraryResources;
+    this.classpathResourceProviders = classpathResourceProviders;
+    this.libraryResourceProviders = libraryResourceProviders;
     this.proguardMap = proguardMap;
     this.proguardSeeds = proguardSeeds;
     this.packageDistribution = packageDistribution;
@@ -140,45 +148,49 @@
   }
 
   /** Get input streams for all dex program resources. */
-  public List<InternalResource> getDexProgramResources() {
-    return filter(dexSources, Resource.Kind.PROGRAM);
+  public List<Resource> getDexProgramResources() {
+    return filter(programResources, Resource.Kind.DEX);
   }
 
   /** Get input streams for all Java-bytecode program resources. */
-  public List<InternalResource> getClassProgramResources() {
-    return filter(classSources, Resource.Kind.PROGRAM);
+  public List<Resource> getClassProgramResources() {
+    return filter(programResources, Resource.Kind.CLASSFILE);
   }
 
   /** Get input streams for all dex program classpath resources. */
-  public List<InternalResource> getDexClasspathResources() {
-    return filter(dexSources, Resource.Kind.CLASSPATH);
+  public List<Resource> getDexClasspathResources() {
+    return filter(classpathResources, Resource.Kind.DEX);
   }
 
   /** Get input streams for all Java-bytecode classpath resources. */
-  public List<InternalResource> getClassClasspathResources() {
-    return filter(classSources, Resource.Kind.CLASSPATH);
+  public List<Resource> getClassClasspathResources() {
+    return filter(classpathResources, Resource.Kind.CLASSFILE);
   }
 
   /** Get input streams for all dex library resources. */
-  public List<InternalResource> getDexLibraryResources() {
-    return filter(dexSources, Resource.Kind.LIBRARY);
+  public List<Resource> getDexLibraryResources() {
+    return filter(libraryResources, Resource.Kind.DEX);
   }
 
   /** Get input streams for all Java-bytecode library resources. */
-  public List<InternalResource> getClassLibraryResources() {
-    return filter(classSources, Resource.Kind.LIBRARY);
+  public List<Resource> getClassLibraryResources() {
+    return filter(libraryResources, Resource.Kind.CLASSFILE);
   }
 
-  /** Get lazy resource providers. */
-  public List<ResourceProvider> getLazyResourceProviders() {
-    return resourceProviders;
+  /** Get classpath resource providers. */
+  public List<ResourceProvider> getClasspathResourceProviders() {
+    return classpathResourceProviders;
   }
 
-  private List<InternalResource> filter(
-      List<InternalResource> resources, Resource.Kind kind) {
-    List<InternalResource> out = new ArrayList<>(resources.size());
-    for (InternalResource resource : resources) {
-      if (kind == resource.getKind()) {
+  /** Get library resource providers. */
+  public List<ResourceProvider> getLibraryResourceProviders() {
+    return libraryResourceProviders;
+  }
+
+  private List<Resource> filter(List<Resource> resources, Resource.Kind kind) {
+    List<Resource> out = new ArrayList<>(resources.size());
+    for (Resource resource : resources) {
+      if (kind == resource.kind) {
         out.add(resource);
       }
     }
@@ -273,7 +285,7 @@
       Path directory, OutputMode outputMode, boolean overwrite) throws IOException {
     CopyOption[] options = copyOptions(overwrite);
     try (Closer closer = Closer.create()) {
-      List<InternalResource> dexProgramSources = getDexProgramResources();
+      List<Resource> dexProgramSources = getDexProgramResources();
       for (int i = 0; i < dexProgramSources.size(); i++) {
         Path fileName = directory.resolve(outputMode.getFileName(dexProgramSources.get(i), i));
         Files.copy(dexProgramSources.get(i).getStream(closer), fileName, options);
@@ -286,7 +298,7 @@
   public List<byte[]> writeToMemory() throws IOException {
     List<byte[]> dex = new ArrayList<>();
     try (Closer closer = Closer.create()) {
-      List<InternalResource> dexProgramSources = getDexProgramResources();
+      List<Resource> dexProgramSources = getDexProgramResources();
       for (int i = 0; i < dexProgramSources.size(); i++) {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         ByteStreams.copy(dexProgramSources.get(i).getStream(closer), out);
@@ -312,7 +324,7 @@
     OpenOption[] options = openOptions(overwrite);
     try (Closer closer = Closer.create()) {
       try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
-        List<InternalResource> dexProgramSources = getDexProgramResources();
+        List<Resource> dexProgramSources = getDexProgramResources();
         for (int i = 0; i < dexProgramSources.size(); i++) {
           ZipEntry zipEntry = new ZipEntry(outputMode.getFileName(dexProgramSources.get(i), i));
           byte[] bytes = ByteStreams.toByteArray(dexProgramSources.get(i).getStream(closer));
@@ -368,13 +380,15 @@
    */
   public static class Builder {
 
-    private final List<InternalResource> dexSources = new ArrayList<>();
-    private final List<InternalResource> classSources = new ArrayList<>();
-    private final List<ResourceProvider> resourceProviders = new ArrayList<>();
-    private InternalResource proguardMap;
-    private InternalResource proguardSeeds;
-    private InternalResource packageDistribution;
-    private InternalResource mainDexList;
+    private final List<Resource> programResources = new ArrayList<>();
+    private final List<Resource> classpathResources = new ArrayList<>();
+    private final List<Resource> libraryResources = new ArrayList<>();
+    private final List<ResourceProvider> classpathResourceProviders = new ArrayList<>();
+    private final List<ResourceProvider> libraryResourceProviders = new ArrayList<>();
+    private Resource proguardMap;
+    private Resource proguardSeeds;
+    private Resource packageDistribution;
+    private Resource mainDexList;
 
     // See AndroidApp::builder().
     private Builder() {
@@ -382,9 +396,11 @@
 
     // See AndroidApp::builder(AndroidApp).
     private Builder(AndroidApp app) {
-      dexSources.addAll(app.dexSources);
-      classSources.addAll(app.classSources);
-      resourceProviders.addAll(app.resourceProviders);
+      programResources.addAll(app.programResources);
+      classpathResources.addAll(app.classpathResources);
+      libraryResources.addAll(app.libraryResources);
+      classpathResourceProviders.addAll(app.classpathResourceProviders);
+      libraryResourceProviders.addAll(app.libraryResourceProviders);
       proguardMap = app.proguardMap;
       proguardSeeds = app.proguardSeeds;
       packageDistribution = app.packageDistribution;
@@ -405,7 +421,7 @@
     public Builder addProgramDirectory(Path directory) throws IOException {
       File[] resources = directory.toFile().listFiles(file -> isDexFile(file.toPath()));
       for (File source : resources) {
-        addFile(source.toPath(), Resource.Kind.PROGRAM);
+        addFile(source.toPath(), ClassKind.PROGRAM);
       }
       File mapFile = new File(directory.toFile(), DEFAULT_PROGUARD_MAP_FILE);
       if (mapFile.exists()) {
@@ -426,7 +442,7 @@
      */
     public Builder addProgramFiles(Collection<Path> files) throws IOException {
       for (Path file : files) {
-        addFile(file, Resource.Kind.PROGRAM);
+        addFile(file, ClassKind.PROGRAM);
       }
       return this;
     }
@@ -443,7 +459,7 @@
      */
     public Builder addClasspathFiles(Collection<Path> files) throws IOException {
       for (Path file : files) {
-        addFile(file, Resource.Kind.CLASSPATH);
+        addFile(file, ClassKind.CLASSPATH);
       }
       return this;
     }
@@ -452,7 +468,7 @@
      * Add classpath resource provider.
      */
     public Builder addClasspathResourceProvider(ResourceProvider provider) {
-      resourceProviders.add(provider);
+      classpathResourceProviders.add(provider);
       return this;
     }
 
@@ -468,7 +484,7 @@
      */
     public Builder addLibraryFiles(Collection<Path> files) throws IOException {
       for (Path file : files) {
-        addFile(file, Resource.Kind.LIBRARY);
+        addFile(file, ClassKind.LIBRARY);
       }
       return this;
     }
@@ -477,7 +493,7 @@
      * Add library resource provider.
      */
     public Builder addLibraryResourceProvider(ResourceProvider provider) {
-      resourceProviders.add(provider);
+      libraryResourceProviders.add(provider);
       return this;
     }
 
@@ -485,7 +501,8 @@
      * Add dex program-data with class descriptor.
      */
     public Builder addDexProgramData(byte[] data, Set<String> classDescriptors) {
-      dexSources.add(InternalResource.fromBytes(Resource.Kind.PROGRAM, data, classDescriptors));
+      resources(ClassKind.PROGRAM).add(
+          Resource.fromBytes(Resource.Kind.DEX, data, classDescriptors));
       return this;
     }
 
@@ -501,7 +518,7 @@
      */
     public Builder addDexProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
-        dexSources.add(InternalResource.fromBytes(Resource.Kind.PROGRAM, datum));
+        resources(ClassKind.PROGRAM).add(Resource.fromBytes(Resource.Kind.DEX, datum));
       }
       return this;
     }
@@ -518,7 +535,7 @@
      */
     public Builder addClassProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
-        classSources.add(InternalResource.fromBytes(Resource.Kind.PROGRAM, datum));
+        resources(ClassKind.PROGRAM).add(Resource.fromBytes(Resource.Kind.CLASSFILE, datum));
       }
       return this;
     }
@@ -527,7 +544,7 @@
      * Set proguard-map file.
      */
     public Builder setProguardMapFile(Path file) {
-      proguardMap = file == null ? null : InternalResource.fromFile(null, file);
+      proguardMap = file == null ? null : Resource.fromFile(null, file);
       return this;
     }
 
@@ -542,7 +559,7 @@
      * Set proguard-map data.
      */
     public Builder setProguardMapData(byte[] content) {
-      proguardMap = content == null ? null : InternalResource.fromBytes(null, content);
+      proguardMap = content == null ? null : Resource.fromBytes(null, content);
       return this;
     }
 
@@ -550,7 +567,7 @@
      * Set proguard-seeds data.
      */
     public Builder setProguardSeedsData(byte[] content) {
-      proguardSeeds = content == null ? null : InternalResource.fromBytes(null, content);
+      proguardSeeds = content == null ? null : Resource.fromBytes(null, content);
       return this;
     }
 
@@ -558,7 +575,7 @@
      * Set the package-distribution file.
      */
     public Builder setPackageDistributionFile(Path file) {
-      packageDistribution = file == null ? null : InternalResource.fromFile(null, file);
+      packageDistribution = file == null ? null : Resource.fromFile(null, file);
       return this;
     }
 
@@ -566,7 +583,7 @@
      * Set the main-dex list file.
      */
     public Builder setMainDexListFile(Path file) {
-      mainDexList = file == null ? null : InternalResource.fromFile(null, file);
+      mainDexList = file == null ? null : Resource.fromFile(null, file);
       return this;
     }
 
@@ -575,31 +592,45 @@
      */
     public AndroidApp build() {
       return new AndroidApp(
-          ImmutableList.copyOf(dexSources),
-          ImmutableList.copyOf(classSources),
-          ImmutableList.copyOf(resourceProviders),
+          ImmutableList.copyOf(programResources),
+          ImmutableList.copyOf(classpathResources),
+          ImmutableList.copyOf(libraryResources),
+          ImmutableList.copyOf(classpathResourceProviders),
+          ImmutableList.copyOf(libraryResourceProviders),
           proguardMap,
           proguardSeeds,
           packageDistribution,
           mainDexList);
     }
 
-    private void addFile(Path file, Resource.Kind kind) throws IOException {
+    private List<Resource> resources(ClassKind classKind) {
+      switch (classKind) {
+        case PROGRAM:
+          return programResources;
+        case CLASSPATH:
+          return classpathResources;
+        case LIBRARY:
+          return libraryResources;
+      }
+      throw new Unreachable();
+    }
+
+    private void addFile(Path file, ClassKind classKind) throws IOException {
       if (!Files.exists(file)) {
         throw new FileNotFoundException("Non-existent input file: " + file);
       }
       if (isDexFile(file)) {
-        dexSources.add(InternalResource.fromFile(kind, file));
+        resources(classKind).add(Resource.fromFile(Resource.Kind.DEX, file));
       } else if (isClassFile(file)) {
-        classSources.add(InternalResource.fromFile(kind, file));
+        resources(classKind).add(Resource.fromFile(Resource.Kind.CLASSFILE, file));
       } else if (isArchive(file)) {
-        addArchive(file, kind);
+        addArchive(file, classKind);
       } else {
         throw new CompilationError("Unsupported source file type for file: " + file);
       }
     }
 
-    private void addArchive(Path archive, Resource.Kind kind) throws IOException {
+    private void addArchive(Path archive, ClassKind classKind) throws IOException {
       assert isArchive(archive);
       boolean containsDexData = false;
       boolean containsClassData = false;
@@ -609,12 +640,13 @@
           Path name = Paths.get(entry.getName());
           if (isDexFile(name)) {
             containsDexData = true;
-            dexSources.add(InternalResource.fromBytes(kind, ByteStreams.toByteArray(stream)));
+            resources(classKind).add(Resource.fromBytes(
+                Resource.Kind.DEX, ByteStreams.toByteArray(stream)));
           } else if (isClassFile(name)) {
             containsClassData = true;
             String descriptor = PreloadedResourceProvider.guessTypeDescriptor(name);
-            classSources.add(InternalResource.fromBytes(
-                kind, ByteStreams.toByteArray(stream), Collections.singleton(descriptor)));
+            resources(classKind).add(Resource.fromBytes(Resource.Kind.CLASSFILE,
+                ByteStreams.toByteArray(stream), Collections.singleton(descriptor)));
           }
         }
       } catch (ZipException e) {
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryResourceProvider.java b/src/main/java/com/android/tools/r8/utils/DirectoryResourceProvider.java
index 1d2ff81..b003d86 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryResourceProvider.java
@@ -10,13 +10,15 @@
 import java.io.File;
 import java.nio.file.Path;
 
-/** Lazy resource provider based on filesystem directory content. */
+/**
+ * Lazy resource provider based on filesystem directory content.
+ *
+ * NOTE: only handles classfile resources.
+ */
 public final class DirectoryResourceProvider implements ResourceProvider {
-  private final Resource.Kind kind;
   private final Path root;
 
-  private DirectoryResourceProvider(Resource.Kind kind, Path root) {
-    this.kind = kind;
+  private DirectoryResourceProvider(Path root) {
     this.root = root;
   }
 
@@ -30,11 +32,11 @@
     File file = filePath.toFile();
 
     return (file.exists() && !file.isDirectory())
-        ? InternalResource.fromFile(kind, filePath) : null;
+        ? Resource.fromFile(Resource.Kind.CLASSFILE, filePath) : null;
   }
 
   /** Create resource provider from directory path. */
-  public static ResourceProvider fromDirectory(Resource.Kind kind, Path dir) {
-    return new DirectoryResourceProvider(kind, dir.toAbsolutePath());
+  public static ResourceProvider fromDirectory(Path dir) {
+    return new DirectoryResourceProvider(dir.toAbsolutePath());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalResource.java b/src/main/java/com/android/tools/r8/utils/InternalResource.java
deleted file mode 100644
index 9181092..0000000
--- a/src/main/java/com/android/tools/r8/utils/InternalResource.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// 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 com.android.tools.r8.Resource;
-import com.google.common.io.Closer;
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.util.Set;
-
-/**
- * Internal resource class that is not intended for use from the outside.
- *
- * <p>This is only here to hide the creation and class descriptor methods
- * from the javadoc for the D8 API. If we decide to expose those methods
- * later the split between Resource and InternalResource can be removed.
- */
-public abstract class InternalResource implements Resource {
-
-  private final Kind kind;
-
-  private InternalResource(Kind kind) {
-    this.kind = kind;
-  }
-
-  /** Get the kind of the resource. */
-  public Kind getKind() {
-    return kind;
-  }
-
-  /** Create an application resource for a given file and kind. */
-  public static InternalResource fromFile(Kind kind, Path file) {
-    return new FileResource(kind, file);
-  }
-
-  /** Create an application resource for a given content and kind. */
-  public static InternalResource fromBytes(Kind kind, byte[] bytes) {
-    return fromBytes(kind, bytes, null);
-  }
-
-  /** Create an application resource for a given content, kind and type descriptor. */
-  public static InternalResource fromBytes(Kind kind, byte[] bytes, Set<String> typeDescriptors) {
-    return new ByteResource(kind, bytes, typeDescriptors);
-  }
-
-  /**
-   * If the resource represents a single class returns
-   * its descriptor, returns `null` otherwise.
-   */
-  public String getSingleClassDescriptorOrNull() {
-    Set<String> descriptors = getClassDescriptors();
-    return (descriptors == null) || (descriptors.size() != 1)
-        ? null : descriptors.iterator().next();
-  }
-
-  /** File based application resource. */
-  private static class FileResource extends InternalResource {
-    final Path file;
-
-    FileResource(Kind kind, Path file) {
-      super(kind);
-      assert file != null;
-      this.file = file;
-    }
-
-    @Override
-    public Set<String> getClassDescriptors() {
-      return null;
-    }
-
-    @Override
-    public InputStream getStream(Closer closer) throws IOException {
-      return closer.register(new FileInputStream(file.toFile()));
-    }
-  }
-
-  /** Byte content based application resource. */
-  private static class ByteResource extends InternalResource {
-    final Set<String> classDescriptors;
-    final byte[] bytes;
-
-    ByteResource(Kind kind, byte[] bytes, Set<String> classDescriptors) {
-      super(kind);
-      assert bytes != null;
-      this.classDescriptors = classDescriptors;
-      this.bytes = bytes;
-    }
-
-    @Override
-    public Set<String> getClassDescriptors() {
-      return classDescriptors;
-    }
-
-    @Override
-    public InputStream getStream(Closer closer) throws IOException {
-      // Note: closing a byte-array input stream is a no-op.
-      return new ByteArrayInputStream(bytes);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java b/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
index 4f36811..0e71973 100644
--- a/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/LazyClassCollection.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.ResourceProvider;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassPromise;
@@ -39,7 +40,8 @@
   private final Map<DexType, DexClassPromise> classes = new IdentityHashMap<>();
 
   // Available lazy resource providers.
-  private final List<ResourceProvider> providers;
+  private final List<ResourceProvider> classpathProviders;
+  private final List<ResourceProvider> libraryProviders;
 
   // Application reader to be used. Note that the reader may be reused in
   // many loaders and may be used concurrently, it is considered to be
@@ -48,8 +50,10 @@
   // which is thread-safe).
   private final JarApplicationReader reader;
 
-  public LazyClassCollection(JarApplicationReader reader, List<ResourceProvider> providers) {
-    this.providers = ImmutableList.copyOf(providers);
+  public LazyClassCollection(JarApplicationReader reader,
+      List<ResourceProvider> classpathProviders, List<ResourceProvider> libraryProviders) {
+    this.classpathProviders = ImmutableList.copyOf(classpathProviders);
+    this.libraryProviders = ImmutableList.copyOf(libraryProviders);
     this.reader = reader;
   }
 
@@ -92,25 +96,30 @@
   // provided resource for this type.
   private DexClassPromise buildPromiseChain(DexType type) {
     String descriptor = type.descriptor.toString();
-    DexClassPromise promise = null;
-    int size = providers.size();
-    for (int i = size - 1; i >= 0; i--) {
-      Resource resource = providers.get(i).getResource(descriptor);
+    DexClassPromise promise = buildPromiseChain(
+        type, descriptor, null, classpathProviders, ClassKind.CLASSPATH);
+    promise = buildPromiseChain(
+        type, descriptor, promise, libraryProviders, ClassKind.LIBRARY);
+    return promise == null ? EmptyPromise.INSTANCE : promise;
+  }
+
+  private DexClassPromise buildPromiseChain(DexType type, String descriptor,
+      DexClassPromise promise, List<ResourceProvider> providers, ClassKind classKind) {
+    for (ResourceProvider provider : providers) {
+      Resource resource = provider.getResource(descriptor);
       if (resource == null) {
         continue;
       }
 
-      if (resource.getKind() == Resource.Kind.PROGRAM) {
-        throw new CompilationError("Attempt to load program class " +
-            type.toSourceString() + " via lazy resource provider");
+      if (resource.kind != Resource.Kind.CLASSFILE) {
+        throw new CompilationError("Resource returned by resource provider for type " +
+            type.toSourceString() + " must be a class file resource.");
       }
 
-      assert resource instanceof InternalResource;
-      LazyClassFileLoader loader =
-          new LazyClassFileLoader(type, (InternalResource) resource, reader);
+      LazyClassFileLoader loader = new LazyClassFileLoader(type, resource, classKind, reader);
       promise = (promise == null) ? loader : new DexClassPromiseChain(loader, promise);
     }
-    return promise == null ? EmptyPromise.INSTANCE : promise;
+    return promise;
   }
 
   // Chooses the proper promise. Recursion is not expected to be deep.
@@ -130,7 +139,7 @@
     }
 
     @Override
-    public DexClass.Origin getOrigin() {
+    public Resource.Kind getOrigin() {
       throw new Unreachable();
     }
 
@@ -172,7 +181,7 @@
     }
 
     @Override
-    public DexClass.Origin getOrigin() {
+    public Resource.Kind getOrigin() {
       return promise.getOrigin();
     }
 
diff --git a/src/main/java/com/android/tools/r8/utils/OutputMode.java b/src/main/java/com/android/tools/r8/utils/OutputMode.java
index c27429a..ce743e8 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputMode.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputMode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.Resource;
+import java.util.Set;
 
 /** Defines way the output is formed. */
 public enum OutputMode {
@@ -16,9 +17,10 @@
   FilePerClass {
     @Override
     String getFileName(Resource resource, int index) {
-      assert resource instanceof InternalResource;
-      String classDescriptor = ((InternalResource) resource).getSingleClassDescriptorOrNull();
-      assert classDescriptor != null;
+      Set<String> classDescriptors = resource.getClassDescriptors();
+      assert classDescriptors != null;
+      assert classDescriptors.size() == 1;
+      String classDescriptor = classDescriptors.iterator().next();
       assert !classDescriptor.contains(".");
       return DescriptorUtils.descriptorToJavaType(classDescriptor) + ".dex";
     }
diff --git a/src/main/java/com/android/tools/r8/utils/PreloadedResourceProvider.java b/src/main/java/com/android/tools/r8/utils/PreloadedResourceProvider.java
index e3d5652..fcb89d4 100644
--- a/src/main/java/com/android/tools/r8/utils/PreloadedResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/PreloadedResourceProvider.java
@@ -22,13 +22,15 @@
 import java.util.zip.ZipException;
 import java.util.zip.ZipInputStream;
 
-/** Lazy resource provider based on preloaded/prebuilt context. */
+/**
+ * Lazy resource provider based on preloaded/prebuilt context.
+ *
+ * NOTE: only handles classfile resources.
+ */
 public final class PreloadedResourceProvider implements ResourceProvider {
-  private final Resource.Kind kind;
   private final Map<String, byte[]> content;
 
-  private PreloadedResourceProvider(Resource.Kind kind, Map<String, byte[]> content) {
-    this.kind = kind;
+  private PreloadedResourceProvider(Map<String, byte[]> content) {
     this.content = content;
   }
 
@@ -38,12 +40,11 @@
     if (bytes == null) {
       return null;
     }
-    return InternalResource.fromBytes(kind, bytes, Collections.singleton(descriptor));
+    return Resource.fromBytes(Resource.Kind.CLASSFILE, bytes, Collections.singleton(descriptor));
   }
 
   /** Create preloaded content resource provider from archive file. */
-  public static ResourceProvider fromArchive(
-      Resource.Kind kind, Path archive) throws IOException {
+  public static ResourceProvider fromArchive(Path archive) throws IOException {
     assert isArchive(archive);
     Builder builder = builder();
     try (ZipInputStream stream = new ZipInputStream(new FileInputStream(archive.toFile()))) {
@@ -59,7 +60,7 @@
           "Zip error while reading '" + archive + "': " + e.getMessage(), e);
     }
 
-    return builder.build(kind);
+    return builder.build();
   }
 
   // Guess class descriptor from location of the class file.
@@ -99,10 +100,9 @@
       return this;
     }
 
-    public PreloadedResourceProvider build(Resource.Kind kind) {
+    public PreloadedResourceProvider build() {
       assert content != null;
-      assert kind != null;
-      PreloadedResourceProvider provider = new PreloadedResourceProvider(kind, content);
+      PreloadedResourceProvider provider = new PreloadedResourceProvider(content);
       content = null;
       return provider;
     }
diff --git a/src/test/examples/regress_62300145/Regress.java b/src/test/examples/regress_62300145/Regress.java
index 77aa6bd..f151e83 100644
--- a/src/test/examples/regress_62300145/Regress.java
+++ b/src/test/examples/regress_62300145/Regress.java
@@ -3,9 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package regress_62300145;
 
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
 import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.lang.reflect.Constructor;
 
 public class Regress {
@@ -13,13 +17,18 @@
   public @interface A {
   }
 
+  @Retention(CLASS)
+  @Target({ElementType.PARAMETER})
+  public @interface B {
+  }
+
   public class InnerClass {
-    public InnerClass(@A String p) {}
+    public InnerClass(@A @B String p1, @A String p2, @B String p3) { }
   }
 
   public static void main(String[] args) throws NoSuchMethodException {
-    Constructor<InnerClass> constructor =
-        InnerClass.class.getDeclaredConstructor(Regress.class, String.class);
+    Constructor<InnerClass> constructor = InnerClass.class.getDeclaredConstructor(
+        Regress.class, String.class, String.class, String.class);
     int i = 0;
     for (Annotation[] annotations : constructor.getParameterAnnotations()) {
       System.out.print(i++ + ": ");
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index e2e88e2..728f769 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalResource;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.android.tools.r8.utils.OutputMode;
 import com.beust.jcommander.internal.Lists;
@@ -26,6 +25,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.function.UnaryOperator;
@@ -74,9 +74,11 @@
       List<String> classFiles = collectClassFiles(testJarFile);
       AndroidApp app = compileClassFiles(
           testJarFile, classFiles, output, OutputMode.FilePerClass);
-      for (InternalResource resource : app.getDexProgramResources()) {
-        String classDescriptor = resource.getSingleClassDescriptorOrNull();
-        Assert.assertNotNull("Add resources are expected to have a descriptor", classDescriptor);
+      for (Resource resource : app.getDexProgramResources()) {
+        Set<String> descriptors = resource.getClassDescriptors();
+        Assert.assertNotNull(descriptors);
+        Assert.assertEquals(1, descriptors.size());
+        String classDescriptor = descriptors.iterator().next();
         classDescriptor = classDescriptor.substring(1, classDescriptor.length() - 1);
         fileToResource.put(classDescriptor + ".class", resource);
       }
diff --git a/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java
index b7181b8..7cef1a4 100644
--- a/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8ResourceProviderRunExamplesAndroidOTest.java
@@ -25,14 +25,13 @@
 
     private void addClasspathPath(Path location, D8Command.Builder builder) {
       builder.addClasspathResourceProvider(
-          DirectoryResourceProvider.fromDirectory(
-              Resource.Kind.CLASSPATH, location.resolve("..")));
+          DirectoryResourceProvider.fromDirectory(location.resolve("..")));
     }
 
     @Override
     void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
       builder.addLibraryResourceProvider(
-          PreloadedResourceProvider.fromArchive(Resource.Kind.LIBRARY, location));
+          PreloadedResourceProvider.fromArchive(location));
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
new file mode 100644
index 0000000..de72fb2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
@@ -0,0 +1,29 @@
+// 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.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8PhotosVerificationTest extends CompilationTestBase {
+  public static final String PHOTOS =
+      "third_party/photos/2017-06-06/PhotosEnglishOnlyLegacy_proguard.jar";
+
+  public void runD8AndCheckVerification(CompilationMode mode, String version)
+      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+    runAndCheckVerification(
+        CompilerUnderTest.D8, mode, version, null, null, version);
+  }
+
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runD8AndCheckVerification(CompilationMode.RELEASE, PHOTOS);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index d9314d8..2bc8dba 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -8,10 +8,10 @@
 
 import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
+import com.android.tools.r8.Resource;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalResource;
 import com.android.tools.r8.utils.OutputMode;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
@@ -43,8 +43,8 @@
 
     // Verify that the result of the two compilations was the same.
     try (Closer closer = Closer.create()) {
-      List<InternalResource> files1 = app1.getDexProgramResources();
-      List<InternalResource> files2 = app2.getDexProgramResources();
+      List<Resource> files1 = app1.getDexProgramResources();
+      List<Resource> files2 = app2.getDexProgramResources();
       assertEquals(files1.size(), files2.size());
       for (int index = 0; index < files1.size(); index++) {
         InputStream file1 = files1.get(index).getStream(closer);
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
index fecc360..d28c37d 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalResource;
+import com.android.tools.r8.Resource;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
@@ -33,7 +33,7 @@
         ImmutableList.of());
     int bytes = 0;
     try (Closer closer = Closer.create()) {
-      for (InternalResource dex : app.getDexProgramResources()) {
+      for (Resource dex : app.getDexProgramResources()) {
         bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
       }
     }
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index 8b5b814..b26d1aa 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalResource;
+import com.android.tools.r8.Resource;
 import com.google.common.collect.ImmutableList;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
@@ -35,7 +35,7 @@
         ImmutableList.of());
     int bytes = 0;
     try (Closer closer = Closer.create()) {
-      for (InternalResource dex : app.getDexProgramResources()) {
+      for (Resource dex : app.getDexProgramResources()) {
         bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
       }
     }
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 29cef19..44c2156 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -24,7 +24,6 @@
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexAnnotationSetRefList;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -291,7 +290,7 @@
       builder.addClassPromise(
           new DexProgramClass(
               type,
-              DexClass.Origin.Synthetic,
+              null,
               new DexAccessFlags(),
               factory.objectType,
               DexTypeList.empty(),
diff --git a/third_party/photos/2017-06-06.tar.gz.sha1 b/third_party/photos/2017-06-06.tar.gz.sha1
new file mode 100644
index 0000000..b21e5a6
--- /dev/null
+++ b/third_party/photos/2017-06-06.tar.gz.sha1
@@ -0,0 +1 @@
+80389d76881463daf28b47f402c5013f499966bf
\ No newline at end of file