Merge "Disallow the use of filled-new-array of objects before kitkat."
diff --git a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
index 0313cf8..ae022a5 100644
--- a/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/ArchiveClassFileProvider.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.FileUtils.isArchive;
 import static com.android.tools.r8.utils.FileUtils.isClassFile;
 
+import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -23,11 +24,24 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
-/**
- * Lazy Java class file resource provider loading class files form a zip archive.
- */
+/** Lazy Java class file resource provider loading class files form a zip archive. */
 public class ArchiveClassFileProvider implements ClassFileResourceProvider, Closeable {
 
+  private static class ArchiveEntryOrigin extends Origin {
+    final String descriptor;
+
+    public ArchiveEntryOrigin(String descriptor, Origin parent) {
+      super(parent);
+      this.descriptor = descriptor;
+    }
+
+    @Override
+    public String part() {
+      return descriptor;
+    }
+  }
+
+  private final Origin origin;
   private final Set<String> descriptors = new HashSet<>();
   private final ZipFile zipFile;
 
@@ -42,6 +56,7 @@
 
   private ArchiveClassFileProvider(FilteredClassPath archive) throws IOException {
     assert isArchive(archive.getPath());
+    origin = new Resource.PathOrigin(archive.getPath(), Origin.root());
     zipFile = new ZipFile(archive.getPath().toFile());
     final Enumeration<? extends ZipEntry> entries = zipFile.entries();
     while (entries.hasMoreElements()) {
@@ -67,7 +82,7 @@
 
     try (InputStream inputStream = zipFile.getInputStream(getZipEntryFromDescriptor(descriptor))) {
       return Resource.fromBytes(
-          Resource.Kind.CLASSFILE,
+          new ArchiveEntryOrigin(descriptor, origin),
           ByteStreams.toByteArray(inputStream),
           Collections.singleton(descriptor));
     } catch (IOException e) {
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index 9239a9a..58ea240 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -97,7 +97,8 @@
     Map<String, Integer> result = new HashMap<>();
     try (Closer closer = Closer.create()) {
       for (Resource resource : app.getDexProgramResources()) {
-        for (Segment segment: DexFileReader.parseMapFrom(closer.register(resource.getStream()))) {
+        for (Segment segment :
+            DexFileReader.parseMapFrom(closer.register(resource.getStream()), resource.origin)) {
           int value = result.computeIfAbsent(segment.typeName(), (key) -> 0);
           result.put(segment.typeName(), value + segment.size());
         }
diff --git a/src/main/java/com/android/tools/r8/Diagnostic.java b/src/main/java/com/android/tools/r8/Diagnostic.java
index 3991c4f..1c1961d 100644
--- a/src/main/java/com/android/tools/r8/Diagnostic.java
+++ b/src/main/java/com/android/tools/r8/Diagnostic.java
@@ -3,9 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.Resource.Origin;
+
 /**
  * Interface for all diagnostic message produced by D8 and R8.
  */
 public interface Diagnostic {
+  Origin getOrigin();
+
   String getDiagnosticMessage();
 }
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 3548644..8e69699 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.Resource.Origin;
+
 /**
  * A DiagnosticsHandler can be provided to customize handling of diagnostics information.
  *
@@ -16,6 +18,11 @@
    * @param warning Diagnostic containing warning information.
    */
   default void warning(Diagnostic warning) {
+    if (warning.getOrigin() != Origin.unknown()) {
+      System.err.print("Warning in " + warning.getOrigin() + ":\n  ");
+    } else {
+      System.err.print("Warning: ");
+    }
     System.err.println(warning.getDiagnosticMessage());
   }
 
@@ -25,6 +32,9 @@
    * @param info Diagnostic containing the information.
    */
   default void info(Diagnostic info) {
+    if (info.getOrigin() != Origin.unknown()) {
+      System.out.print("In " + info.getOrigin() + ":\n  ");
+    }
     System.out.println(info.getDiagnosticMessage());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
index 56b511d..b2bc53d 100644
--- a/src/main/java/com/android/tools/r8/Resource.java
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -9,35 +9,176 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 /** Represents application resources. */
 public abstract class Resource {
-  /** Kind of the resource describing the resource content. */
-  public enum Kind {
-    DEX, CLASSFILE, VDEX
+
+  /**
+   * Origin description of a resource.
+   *
+   * <p>An origin is a list of parts that describe where a resource originates from. The first part
+   * is the most recent part and is associated with the present resource, each successive part is
+   * then associated with the context of the previous part.
+   *
+   * <p>For example, for a class file, say {@code my/class/Foo.class}, that is contained within a
+   * jar archive, say {@code myjar.jar}, the Origin of of this resource will be {@code
+   * myjar.jar:my/class/Foo.class} where each part is separated by a colon.
+   *
+   * <p>There are two top-most origins which have no parent: {@code Origin.root()} and {@code
+   * Origin.unknown()}. The former is the parent of any file path, while the latter is an unknown
+   * origin (e.g., for generated resources of raw bytes).
+   */
+  public abstract static class Origin implements Comparable<Origin> {
+
+    private static final Origin ROOT =
+        new Origin() {
+          @Override
+          public String part() {
+            return "";
+          }
+
+          @Override
+          List<String> buildParts(int size) {
+            return new ArrayList<>(size);
+          }
+        };
+
+    private static final Origin UNKNOWN =
+        new Origin() {
+          @Override
+          public String part() {
+            return "<unknown>";
+          }
+
+          @Override
+          List<String> buildParts(int size) {
+            List<String> parts = new ArrayList<>(size + 1);
+            parts.add(part());
+            return parts;
+          }
+        };
+
+    public static Origin root() {
+      return ROOT;
+    }
+
+    public static Origin unknown() {
+      return UNKNOWN;
+    }
+
+    private final Origin parent;
+
+    private Origin() {
+      this.parent = null;
+    }
+
+    protected Origin(Origin parent) {
+      assert parent != null;
+      this.parent = parent;
+    }
+
+    public abstract String part();
+
+    public Origin parent() {
+      return parent;
+    }
+
+    public List<String> parts() {
+      return buildParts(0);
+    }
+
+    List<String> buildParts(int size) {
+      List<String> parts = parent().buildParts(size + 1);
+      parts.add(part());
+      return parts;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj == this) {
+        return true;
+      }
+      if (!(obj instanceof Origin)) {
+        return false;
+      }
+      Origin self = this;
+      Origin other = (Origin) obj;
+      while (self != null && other != null && self.part().equals(other.part())) {
+        self = self.parent();
+        other = other.parent();
+      }
+      return self == other;
+    }
+
+    @Override
+    public int compareTo(Origin other) {
+      // Lexicographic ordering from root to leaf.
+      List<String> thisParts = parts();
+      List<String> otherParts = other.parts();
+      int len = Math.min(thisParts.size(), otherParts.size());
+      for (int i = 0; i < len; i++) {
+        int compare = thisParts.get(i).compareTo(otherParts.get(i));
+        if (compare != 0) {
+          return compare;
+        }
+      }
+      return Integer.compare(thisParts.size(), otherParts.size());
+    }
+
+    @Override
+    public int hashCode() {
+      int hash = 1;
+      for (String part : parts()) {
+        hash = 31 * hash + part.hashCode();
+      }
+      return hash;
+    }
+
+    @Override
+    public String toString() {
+      return String.join(":", parts());
+    }
   }
 
-  protected Resource(Kind kind) {
-    this.kind = kind;
+  /** Path component in an origin description. */
+  public static class PathOrigin extends Origin {
+    private final Path path;
+
+    public PathOrigin(Path path, Origin parent) {
+      super(parent);
+      assert path != null;
+      this.path = path;
+    }
+
+    @Override
+    public String part() {
+      return path.toString();
+    }
   }
 
-  /** Kind of the resource. */
-  public final Kind kind;
+  /** Origin of the resource. */
+  public final Origin origin;
+
+  protected Resource(Origin origin) {
+    this.origin = origin;
+  }
 
   /** Create an application resource for a given file. */
-  public static Resource fromFile(Kind kind, Path file) {
-    return new FileResource(kind, file);
+  public static Resource fromFile(Path file) {
+    return new FileResource(file);
   }
 
   /** Create an application resource for a given content. */
-  public static Resource fromBytes(Kind kind, byte[] bytes) {
-    return fromBytes(kind, bytes, null);
+  public static Resource fromBytes(Origin origin, byte[] bytes) {
+    return fromBytes(origin, 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);
+  public static Resource fromBytes(Origin origin, byte[] bytes, Set<String> typeDescriptors) {
+    return new ByteResource(origin, bytes, typeDescriptors);
   }
 
   /**
@@ -49,12 +190,16 @@
   /** Get the resource as a stream. */
   public abstract InputStream getStream() throws IOException;
 
-  /** File based application resource. */
+  /**
+   * File-based application resource.
+   *
+   * <p>The origin of a file resource is the path of the file.
+   */
   private static class FileResource extends Resource {
     final Path file;
 
-    FileResource(Kind kind, Path file) {
-      super(kind);
+    FileResource(Path file) {
+      super(new PathOrigin(file, Origin.root()));
       assert file != null;
       this.file = file;
     }
@@ -70,13 +215,18 @@
     }
   }
 
-  /** Byte content based application resource. */
+  /**
+   * Byte-content based application resource.
+   *
+   * <p>The origin of a byte resource must be supplied upon construction. If no reasonable origin
+   * exits, use {@code Origin.unknown()}.
+   */
   private static class ByteResource extends Resource {
     final Set<String> classDescriptors;
     final byte[] bytes;
 
-    ByteResource(Kind kind, byte[] bytes, Set<String> classDescriptors) {
-      super(kind);
+    ByteResource(Origin origin, byte[] bytes, Set<String> classDescriptors) {
+      super(origin);
       assert bytes != null;
       this.classDescriptors = classDescriptors;
       this.bytes = 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 d389a8c..3e4d28d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -6,7 +6,6 @@
 import static com.android.tools.r8.graph.ClassKind.CLASSPATH;
 import static com.android.tools.r8.graph.ClassKind.LIBRARY;
 import static com.android.tools.r8.graph.ClassKind.PROGRAM;
-import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
 
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.Resource;
@@ -24,10 +23,10 @@
 import com.android.tools.r8.naming.ProguardMapReader;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.ClassProvider;
 import com.android.tools.r8.utils.ClasspathClassCollection;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexVersion;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.LibraryClassCollection;
 import com.android.tools.r8.utils.MainDexList;
@@ -167,7 +166,7 @@
           try (InputStream is = input.getStream()) {
             DexFile file = new DexFile(is);
             computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
-            fileReaders.add(new DexFileReader(file, classKind, itemFactory));
+            fileReaders.add(new DexFileReader(input.origin, file, classKind, itemFactory));
           }
         }
         options.minApiLevel = computedMinApiLevel;
@@ -195,7 +194,7 @@
       for (Resource input : classSources) {
         futures.add(executorService.submit(() -> {
           try (InputStream is = input.getStream()) {
-            reader.read(DEFAULT_DEX_FILENAME, classKind, is);
+            reader.read(input.origin, 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.
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 f3542be..8175834 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -8,7 +8,8 @@
 import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
 import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
 
-import com.android.tools.r8.Resource;
+import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.Resource.PathOrigin;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.InstructionFactory;
 import com.android.tools.r8.graph.ClassKind;
@@ -51,6 +52,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.OffsetToObjectMapping;
 import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import java.io.ByteArrayOutputStream;
@@ -68,21 +70,22 @@
 public class DexFileReader {
 
   final int NO_INDEX = -1;
+  private final Origin origin;
   private DexFile file;
   private final Segment[] segments;
   private int[] stringIDs;
   private final ClassKind classKind;
 
   public static Segment[] parseMapFrom(Path file) throws IOException {
-    return parseMapFrom(new DexFile(file.toString()));
+    return parseMapFrom(new DexFile(file.toString()), new PathOrigin(file, Origin.root()));
   }
 
-  public static Segment[] parseMapFrom(InputStream stream) throws IOException {
-    return parseMapFrom(new DexFile(stream));
+  public static Segment[] parseMapFrom(InputStream stream, Origin origin) throws IOException {
+    return parseMapFrom(new DexFile(stream), origin);
   }
 
-  private static Segment[] parseMapFrom(DexFile dex) {
-    DexFileReader reader = new DexFileReader(dex, ClassKind.PROGRAM, new DexItemFactory());
+  private static Segment[] parseMapFrom(DexFile dex, Origin origin) {
+    DexFileReader reader = new DexFileReader(origin, dex, ClassKind.PROGRAM, new DexItemFactory());
     return reader.segments;
   }
 
@@ -107,7 +110,9 @@
   // Factory to canonicalize certain dexitems.
   private final DexItemFactory dexItemFactory;
 
-  public DexFileReader(DexFile file, ClassKind classKind, DexItemFactory dexItemFactory) {
+  public DexFileReader(
+      Origin origin, DexFile file, ClassKind classKind, DexItemFactory dexItemFactory) {
+    this.origin = origin;
     this.file = file;
     this.dexItemFactory = dexItemFactory;
     file.setByteOrder();
@@ -662,7 +667,8 @@
       }
       clazz = classKind.create(
           type,
-          Resource.Kind.DEX,
+          Kind.DEX,
+          origin,
           flags,
           superclass,
           typeListAt(interfacesOffsets[i]),
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 9298ba5..90644e5 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -1,6 +1,7 @@
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.Resource;
+import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.utils.ProgramResource;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -11,11 +12,19 @@
   LIBRARY(DexLibraryClass::new, DexClass::isLibraryClass);
 
   private interface Factory {
-    DexClass create(DexType type, Resource.Kind origin, DexAccessFlags accessFlags,
+    DexClass create(
+        DexType type,
+        ProgramResource.Kind kind,
+        Origin origin,
+        DexAccessFlags accessFlags,
         DexType superType,
-        DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
-        DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
-        DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods);
+        DexTypeList interfaces,
+        DexString sourceFile,
+        DexAnnotationSet annotations,
+        DexEncodedField[] staticFields,
+        DexEncodedField[] instanceFields,
+        DexEncodedMethod[] directMethods,
+        DexEncodedMethod[] virtualMethods);
   }
 
   private final Factory factory;
@@ -27,12 +36,31 @@
   }
 
   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);
+      DexType type,
+      ProgramResource.Kind kind,
+      Origin origin,
+      DexAccessFlags accessFlags,
+      DexType superType,
+      DexTypeList interfaces,
+      DexString sourceFile,
+      DexAnnotationSet annotations,
+      DexEncodedField[] staticFields,
+      DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods,
+      DexEncodedMethod[] virtualMethods) {
+    return factory.create(
+        type,
+        kind,
+        origin,
+        accessFlags,
+        superType,
+        interfaces,
+        sourceFile,
+        annotations,
+        staticFields,
+        instanceFields,
+        directMethods,
+        virtualMethods);
   }
 
   public boolean isOfKind(DexClass clazz) {
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 e5d4a56..8f3a923 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -3,7 +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.Resource.Origin;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
@@ -17,7 +17,7 @@
   private static final DexEncodedMethod[] NO_METHODS = {};
   private static final DexEncodedField[] NO_FIELDS = {};
 
-  public final Resource.Kind origin;
+  public final Origin origin;
   public final DexType type;
   public final DexAccessFlags accessFlags;
   public DexType superType;
@@ -30,10 +30,17 @@
   public DexAnnotationSet annotations;
 
   public DexClass(
-      DexString sourceFile, DexTypeList interfaces, DexAccessFlags accessFlags, DexType superType,
-      DexType type, DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
-      DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods,
-      DexAnnotationSet annotations, Resource.Kind origin) {
+      DexString sourceFile,
+      DexTypeList interfaces,
+      DexAccessFlags accessFlags,
+      DexType superType,
+      DexType type,
+      DexEncodedField[] staticFields,
+      DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods,
+      DexEncodedMethod[] virtualMethods,
+      DexAnnotationSet annotations,
+      Origin origin) {
     this.origin = origin;
     this.sourceFile = sourceFile;
     this.interfaces = interfaces;
@@ -215,7 +222,7 @@
         .orElse(null);
   }
 
-  public Resource.Kind getOrigin() {
+  public Origin getOrigin() {
     return this.origin;
   }
 
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 27ab302..d39763e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -3,20 +3,32 @@
 // 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.Resource.Origin;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.ProgramResource;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import java.util.function.Supplier;
 
 public class DexClasspathClass extends DexClass implements Supplier<DexClasspathClass> {
 
-  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) {
+  public DexClasspathClass(
+      DexType type,
+      ProgramResource.Kind kind,
+      Origin 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,
         staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+    assert kind == Kind.CLASS : "Invalid kind " + kind + " for class-path class " + type;
   }
 
   @Override
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 ffff701..3c9cbdd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -3,18 +3,29 @@
 // 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.Resource.Origin;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.ProgramResource;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import java.util.function.Supplier;
 
 public class DexLibraryClass extends DexClass implements Supplier<DexLibraryClass> {
 
-  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) {
+  public DexLibraryClass(
+      DexType type,
+      ProgramResource.Kind kind,
+      Origin 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,
         staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
     // Set all static field values to unknown. We don't want to use the value from the library
@@ -22,6 +33,7 @@
     for (DexEncodedField staticField : staticFields) {
       staticField.staticValue = DexValue.UNKNOWN;
     }
+    assert kind == Kind.CLASS : "Invalid kind " + kind + " for library-path class " + type;
   }
 
   @Override
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 4332df8..3c9c796 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,9 +3,11 @@
 // 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.Resource.Origin;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.utils.ProgramResource;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -15,12 +17,14 @@
 import java.util.function.Supplier;
 
 public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
-
+  private final ProgramResource.Kind originKind;
   private DexEncodedArray staticValues;
   private final Collection<DexProgramClass> synthesizedFrom;
 
-  public DexProgramClass(DexType type,
-      Resource.Kind origin,
+  public DexProgramClass(
+      DexType type,
+      ProgramResource.Kind originKind,
+      Origin origin,
       DexAccessFlags accessFlags,
       DexType superType,
       DexTypeList interfaces,
@@ -30,7 +34,9 @@
       DexEncodedField[] instanceFields,
       DexEncodedMethod[] directMethods,
       DexEncodedMethod[] virtualMethods) {
-    this(type,
+    this(
+        type,
+        originKind,
         origin,
         accessFlags,
         superType,
@@ -44,8 +50,10 @@
         Collections.emptyList());
   }
 
-  public DexProgramClass(DexType type,
-      Resource.Kind origin,
+  public DexProgramClass(
+      DexType type,
+      ProgramResource.Kind originKind,
+      Origin origin,
       DexAccessFlags accessFlags,
       DexType superType,
       DexTypeList interfaces,
@@ -59,9 +67,18 @@
     super(sourceFile, interfaces, accessFlags, superType, type, staticFields,
         instanceFields, directMethods, virtualMethods, classAnnotations, origin);
     assert classAnnotations != null;
+    this.originKind = originKind;
     this.synthesizedFrom = accumulateSynthesizedFrom(new HashSet<>(), synthesizedDirectlyFrom);
   }
 
+  public boolean originatesFromDexResource() {
+    return originKind == Kind.DEX;
+  }
+
+  public boolean originatesFromClassResource() {
+    return originKind == Kind.CLASS;
+  }
+
   @Override
   public void collectIndexedItems(IndexedItemCollection indexedItems) {
     if (indexedItems.addClass(this)) {
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 a90373e..001480f 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -7,7 +7,7 @@
 import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
 import static org.objectweb.asm.Opcodes.ASM6;
 
-import com.android.tools.r8.Resource;
+import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import com.android.tools.r8.graph.JarCode.ReparseContext;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -60,10 +61,10 @@
     this.classConsumer = classConsumer;
   }
 
-  public void read(String file, ClassKind classKind, InputStream input) throws IOException {
+  public void read(Origin origin, ClassKind classKind, InputStream input) throws IOException {
     ClassReader reader = new ClassReader(input);
     reader.accept(new CreateDexClassVisitor(
-        file, classKind, reader.b, application, classConsumer), SKIP_FRAMES);
+        origin, classKind, reader.b, application, classConsumer), SKIP_FRAMES);
   }
 
   private static DexAccessFlags createAccessFlags(int access) {
@@ -93,7 +94,7 @@
   }
 
   private static class CreateDexClassVisitor extends ClassVisitor {
-    private final String file;
+    private final Origin origin;
     private final ClassKind classKind;
     private final JarApplicationReader application;
     private final Consumer<DexClass> classConsumer;
@@ -116,13 +117,13 @@
     private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
 
     public CreateDexClassVisitor(
-        String file,
+        Origin origin,
         ClassKind classKind,
         byte[] classCache,
         JarApplicationReader application,
         Consumer<DexClass> classConsumer) {
       super(ASM6);
-      this.file = file;
+      this.origin = origin;
       this.classKind = classKind;
       this.classConsumer = classConsumer;
       this.context.classCache = classCache;
@@ -248,7 +249,8 @@
       }
       DexClass clazz = classKind.create(
           type,
-          Resource.Kind.CLASSFILE, 
+          Kind.CLASS,
+          origin,
           accessFlags,
           superType,
           interfaces,
@@ -512,7 +514,7 @@
       if (!flags.isAbstract()
           && !flags.isNative()
           && parent.classKind == ClassKind.PROGRAM) {
-        code = new JarCode(method, parent.context, parent.application);
+        code = new JarCode(method, parent.origin, parent.context, parent.application);
       }
       DexAnnotationSetRefList parameterAnnotationSets;
       if (parameterAnnotations == null) {
@@ -529,11 +531,8 @@
           && internalOptions.canUseParameterNameAnnotations()) {
         assert parameterFlags != null;
         if (parameterNames.size() != parameterCount) {
-          internalOptions.warningInvalidParameterAnnotations =
-              "Invalid parameter count in MethodParameters attributes of "
-                  + method.toSourceString() + " from '" + parent.file + "'. Found "
-                  + parameterNames.size() + " while expecting " + parameterCount + "."
-                  + " This is likely due to proguard having removed a parameter.";
+          internalOptions.warningInvalidParameterAnnotations(
+              method, parent.origin, parameterCount, parameterNames.size());
         }
         getAnnotations().add(DexAnnotation.createMethodParametersAnnotation(
             parameterNames.toArray(new DexValue[parameterNames.size()]),
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 39c580e..5988ae5 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.ValueNumberGenerator;
@@ -37,13 +38,16 @@
   }
 
   private final DexType clazz;
+  private final Origin origin;
   private MethodNode node;
   private ReparseContext context;
 
   private final JarApplicationReader application;
 
-  public JarCode(DexMethod method, ReparseContext context, JarApplicationReader application) {
+  public JarCode(
+      DexMethod method, Origin origin, ReparseContext context, JarApplicationReader application) {
     this.clazz = method.getHolder();
+    this.origin = origin;
     this.context = context;
     this.application = application;
     context.lookupMap.put(method, this);
@@ -105,7 +109,7 @@
     try {
       return internalBuild(encodedMethod, generator, options);
     } catch (InvalidDebugInfoException e) {
-      options.warningInvalidDebugInfo(encodedMethod, e);
+      options.warningInvalidDebugInfo(encodedMethod, origin, e);
       node.localVariables.clear();
       return internalBuild(encodedMethod, generator, options);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index d3964d1..09371f7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -273,15 +273,7 @@
     DefaultMethodsHelper helper = new DefaultMethodsHelper();
     DexClass definedInterface = rewriter.findDefinitionFor(iface);
     if (definedInterface == null) {
-      String message = "Interface `" + iface.toSourceString()
-              + "` not found. It's needed to make sure desugaring of `"
-              + classToDesugar.toSourceString() + "` is correct. Desugaring will assume that this"
-              + " interface has no default method";
-      if (classToDesugar != implementing) {
-        message += ". This missing interface is declared in the direct hierarchy of `"
-              + implementing.toString() + "`";
-      }
-      rewriter.warnMissingClass(iface, message);
+      rewriter.warnMissingInterface(classToDesugar, implementing, iface);
       return helper.wrapInCollection();
     }
 
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 a851831..2c43bd7 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
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.ApiLevelException;
-import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexCallSite;
@@ -127,7 +126,7 @@
             // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
             // exception but we can not report it as error since it can also be the intended
             // behavior.
-            warnMissingClass(encodedMethod.method, method.holder);
+            warnMissingType(encodedMethod.method, method.holder);
           } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
             // NOTE: we intentionally don't desugar static calls into static interface
             // methods coming from android.jar since it is only possible in case v24+
@@ -149,7 +148,7 @@
             // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
             // exception but we can not report it as error since it can also be the intended
             // behavior.
-            warnMissingClass(encodedMethod.method, method.holder);
+            warnMissingType(encodedMethod.method, method.holder);
           } else if (clazz != null && (clazz.isInterface() && !clazz.isLibraryClass())) {
             // NOTE: we intentionally don't desugar super calls into interface methods
             // coming from android.jar since it is only possible in case v24+ version
@@ -171,7 +170,7 @@
       // NOTE: If the class definition is missing we can't check. Let it be handled as any other
       // missing call target.
       if (holderClass == null) {
-        warnMissingClass(referencedFrom, handle.asMethod().holder);
+        warnMissingType(referencedFrom, handle.asMethod().holder);
       } else if (holderClass.isInterface()) {
         throw new Unimplemented(
             "Desugaring of static interface method handle as in `"
@@ -249,7 +248,7 @@
 
   private static boolean shouldProcess(
       DexProgramClass clazz, Flavor flavour, boolean mustBeInterface) {
-    return (clazz.getOrigin() != Resource.Kind.DEX || flavour == Flavor.IncludeAllResources)
+    return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources)
         && clazz.isInterface() == mustBeInterface;
   }
 
@@ -294,17 +293,44 @@
     return true;
   }
 
-  void warnMissingClass(DexType missing, String message) {
-    // TODO replace by a proper warning mechanic (see b/65154707).
+  public void warnMissingInterface(
+      DexClass classToDesugar, DexClass implementing, DexType missing) {
     // TODO think about using a common deduplicating mechanic with Enqueuer
-    if (reportedMissing.add(missing)) {
-      options.diagnosticsHandler.warning(new StringDiagnostic(message));
+    if (!reportedMissing.add(missing)) {
+      return;
     }
+    StringBuilder builder = new StringBuilder();
+    builder
+        .append("Interface `")
+        .append(missing.toSourceString())
+        .append("` not found. It's needed to make sure desugaring of `")
+        .append(classToDesugar.toSourceString())
+        .append("` is correct. Desugaring will assume that this interface has no default method.");
+    if (classToDesugar != implementing) {
+      builder
+          .append(" This missing interface is declared in the direct hierarchy of `")
+          .append(implementing)
+          .append("`");
+    }
+    options.diagnosticsHandler.warning(
+        new StringDiagnostic(classToDesugar.getOrigin(), builder.toString()));
   }
 
-  private void warnMissingClass(DexItem referencedFrom, DexType clazz) {
-      warnMissingClass(clazz,
-          "Type `" + clazz.toSourceString() + "` was not found, it is required for default or"
-          + " static interface methods desugaring of `" + referencedFrom.toSourceString() + "`");
+  private void warnMissingType(DexMethod referencedFrom, DexType missing) {
+    // TODO think about using a common deduplicating mechanic with Enqueuer
+    if (!reportedMissing.add(missing)) {
+      return;
+    }
+    StringBuilder builder = new StringBuilder();
+    builder
+        .append("Type `")
+        .append(missing.toSourceString())
+        .append("` was not found, ")
+        .append("it is required for default or static interface methods desugaring of `")
+        .append(referencedFrom.toSourceString())
+        .append("`");
+    DexClass referencedFromClass = converter.appInfo.definitionFor(referencedFrom.getHolder());
+    options.diagnosticsHandler.warning(
+        new StringDiagnostic(referencedFromClass.getOrigin(), builder.toString()));
   }
 }
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 02d6dc9..fb3507b 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
@@ -128,6 +128,7 @@
     DexProgramClass companionClass = new DexProgramClass(
         companionClassType,
         null,
+        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 0fe0f99..569d088 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
@@ -120,6 +120,7 @@
     return new DexProgramClass(
         type,
         null,
+        null,
         new DexAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC),
         rewriter.factory.objectType,
         buildInterfaces(),
@@ -129,8 +130,7 @@
         synthesizeInstanceFields(),
         synthesizeDirectMethods(),
         synthesizeVirtualMethods(),
-        synthesizedFrom
-    );
+        synthesizedFrom);
   }
 
   final DexField getCaptureField(int index) {
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 49dcb45..35d2e14 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
@@ -1075,6 +1075,7 @@
     DexProgramClass clazz = new DexProgramClass(
         type,
         null,
+        null,
         accessFlags,
         superType,
         interfaces,
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 80a2393..3cfcc92 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -11,12 +11,13 @@
 import com.android.tools.r8.ArchiveClassFileProvider;
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Kind;
+import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.dex.VDexFile;
 import com.android.tools.r8.dex.VDexFileReader;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.utils.ProgramResource.Kind;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.io.ByteStreams;
@@ -55,7 +56,7 @@
 
   public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
 
-  private final ImmutableList<Resource> programResources;
+  private final ImmutableList<ProgramResource> programResources;
   private final ImmutableMap<Resource, String> programResourcesMainDescriptor;
   private final ImmutableList<ClassFileResourceProvider> classpathResourceProviders;
   private final ImmutableList<ClassFileResourceProvider> libraryResourceProviders;
@@ -70,7 +71,7 @@
 
   // See factory methods and AndroidApp.Builder below.
   private AndroidApp(
-      ImmutableList<Resource> programResources,
+      ImmutableList<ProgramResource> programResources,
       ImmutableMap<Resource, String> programResourcesMainDescriptor,
       ImmutableList<ProgramFileArchiveReader> programFileArchiveReaders,
       ImmutableList<ClassFileResourceProvider> classpathResourceProviders,
@@ -159,11 +160,11 @@
 
   /** Get input streams for all dex program resources. */
   public List<Resource> getDexProgramResources() throws IOException {
-    List<Resource> dexResources = filter(programResources, Resource.Kind.DEX);
+    List<Resource> dexResources = filter(programResources, Kind.DEX);
     for (Resource resource : filter(programResources, Kind.VDEX)) {
       VDexFileReader reader = new VDexFileReader(new VDexFile(resource));
       dexResources.addAll(reader.getDexFiles().stream()
-          .map(bytes -> Resource.fromBytes(Kind.DEX, bytes))
+          .map(bytes -> Resource.fromBytes(resource.origin, bytes))
           .collect(Collectors.toList()));
     }
     for (ProgramFileArchiveReader reader : programFileArchiveReaders) {
@@ -174,12 +175,12 @@
 
   public List<Resource> getDexProgramResourcesForOutput() {
     assert programFileArchiveReaders.isEmpty();
-    return filter(programResources, Resource.Kind.DEX);
+    return filter(programResources, Kind.DEX);
   }
 
   /** Get input streams for all Java-bytecode program resources. */
   public List<Resource> getClassProgramResources() throws IOException {
-    List<Resource> classResources = filter(programResources, Resource.Kind.CLASSFILE);
+    List<Resource> classResources = filter(programResources, Kind.CLASS);
     for (ProgramFileArchiveReader reader : programFileArchiveReaders) {
       classResources.addAll(reader.getClassProgramResources());
     }
@@ -200,11 +201,11 @@
     return programFileArchiveReaders;
   }
 
-  private List<Resource> filter(List<Resource> resources, Resource.Kind kind) {
+  private List<Resource> filter(List<ProgramResource> resources, Kind kind) {
     List<Resource> out = new ArrayList<>(resources.size());
-    for (Resource resource : resources) {
-      if (kind == resource.kind) {
-        out.add(resource);
+    for (ProgramResource code : resources) {
+      if (code.kind == kind) {
+        out.add(code.resource);
       }
     }
     return out;
@@ -440,7 +441,7 @@
    */
   public static class Builder {
 
-    private final List<Resource> programResources = new ArrayList<>();
+    private final List<ProgramResource> programResources = new ArrayList<>();
     private final Map<Resource, String> programResourcesMainDescriptor = new HashMap<>();
     private final List<ProgramFileArchiveReader> programFileArchiveReaders = new ArrayList<>();
     private final List<ClassFileResourceProvider> classpathResourceProviders = new ArrayList<>();
@@ -566,8 +567,7 @@
      * Add dex program-data with class descriptor.
      */
     public Builder addDexProgramData(byte[] data, Set<String> classDescriptors) {
-      programResources.add(
-          Resource.fromBytes(Resource.Kind.DEX, data, classDescriptors));
+      addProgramResources(Kind.DEX, Resource.fromBytes(Origin.unknown(), data, classDescriptors));
       return this;
     }
 
@@ -578,8 +578,8 @@
         byte[] data,
         Set<String> classDescriptors,
         String primaryClassDescriptor) {
-      Resource resource = Resource.fromBytes(Resource.Kind.DEX, data, classDescriptors);
-      programResources.add(resource);
+      Resource resource = Resource.fromBytes(Origin.unknown(), data, classDescriptors);
+      addProgramResources(Kind.DEX, resource);
       programResourcesMainDescriptor.put(resource, primaryClassDescriptor);
       return this;
     }
@@ -596,7 +596,7 @@
      */
     public Builder addDexProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
-        programResources.add(Resource.fromBytes(Resource.Kind.DEX, datum));
+        addProgramResources(Kind.DEX, Resource.fromBytes(Origin.unknown(), datum));
       }
       return this;
     }
@@ -613,12 +613,19 @@
      */
     public Builder addClassProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
-        programResources.add(Resource.fromBytes(Resource.Kind.CLASSFILE, datum));
+        addProgramResources(Kind.CLASS, Resource.fromBytes(Origin.unknown(), datum));
       }
       return this;
     }
 
     /**
+     * Add Java-bytecode program data.
+     */
+    public Builder addClassProgramData(Origin origin, byte[] data) {
+      return addProgramResources(Kind.CLASS, Resource.fromBytes(origin, data));
+    }
+
+    /**
      * Set dead-code data.
      */
     public Builder setDeadCode(byte[] content) {
@@ -639,7 +646,7 @@
      * Set proguard-map file.
      */
     public Builder setProguardMapFile(Path file) {
-      proguardMap = file == null ? null : Resource.fromFile(null, file);
+      proguardMap = file == null ? null : Resource.fromFile(file);
       return this;
     }
 
@@ -680,12 +687,11 @@
         }
         // TODO(sgjesse): Should we just read the file here? This will sacrifice the parallelism
         // in ApplicationReader where all input resources are read in parallel.
-        mainDexListResources.add(Resource.fromFile(null, file));
+        mainDexListResources.add(Resource.fromFile(file));
       }
       return this;
     }
 
-
     /**
      * Add main-dex classes.
      */
@@ -758,11 +764,11 @@
         throw new FileNotFoundException("Non-existent input file: " + file);
       }
       if (isDexFile(file)) {
-        programResources.add(Resource.fromFile(Resource.Kind.DEX, file));
+        addProgramResources(Kind.DEX, Resource.fromFile(file));
       } else if (isVDexFile(file) && isVdexAllowed()) {
-        programResources.add(Resource.fromFile(Resource.Kind.VDEX, file));
+        addProgramResources(Kind.VDEX, Resource.fromFile(file));
       } else if (isClassFile(file)) {
-        programResources.add(Resource.fromFile(Resource.Kind.CLASSFILE, file));
+        addProgramResources(Kind.CLASS, Resource.fromFile(file));
       } else if (isArchive(file)) {
         programFileArchiveReaders
             .add(new ProgramFileArchiveReader(filteredClassPath, ignoreDexInArchive));
@@ -771,6 +777,17 @@
       }
     }
 
+    private Builder addProgramResources(ProgramResource.Kind kind, Resource... resources) {
+      return addProgramResources(kind, Arrays.asList(resources));
+    }
+
+    private Builder addProgramResources(ProgramResource.Kind kind, Collection<Resource> resources) {
+      for (Resource resource : resources) {
+        programResources.add(new ProgramResource(kind, resource));
+      }
+      return this;
+    }
+
     private void addClassProvider(FilteredClassPath classPath,
         List<ClassFileResourceProvider> providerList)
         throws IOException {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassProvider.java b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
index 63a847a..5ad270b 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassProvider.java
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
-
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.errors.CompilationError;
@@ -100,7 +98,7 @@
         try (Closer closer = Closer.create()) {
           JarClassFileReader classReader =
               new JarClassFileReader(reader, classKind.bridgeConsumer(classConsumer));
-          classReader.read(DEFAULT_DEX_FILENAME, classKind, closer.register(resource.getStream()));
+          classReader.read(resource.origin, classKind, closer.register(resource.getStream()));
         } catch (IOException e) {
           throw new CompilationError("Failed to load class: " + descriptor, e);
         }
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/DirectoryClassFileProvider.java
index 7de53d2..eba7f8e 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryClassFileProvider.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.Resource;
 import com.google.common.collect.Sets;
 import java.io.File;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.HashSet;
 import java.util.Set;
@@ -57,11 +58,9 @@
 
     // Build expected file path based on type descriptor.
     String classBinaryName = DescriptorUtils.getClassBinaryNameFromDescriptor(descriptor);
-    Path filePath = root.resolve(classBinaryName + CLASS_EXTENSION);
-    File file = filePath.toFile();
+    Path file = root.resolve(classBinaryName + CLASS_EXTENSION);
 
-    return (file.exists() && !file.isDirectory())
-        ? Resource.fromFile(Resource.Kind.CLASSFILE, filePath) : null;
+    return (Files.exists(file) && !Files.isDirectory(file)) ? Resource.fromFile(file) : null;
   }
 
   /** Create resource provider from directory path. */
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index 3eaa2be..d2a4239 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -25,7 +25,6 @@
   public static final String VDEX_EXTENSION = ".vdex";
   public static final String JAR_EXTENSION = ".jar";
   public static final String ZIP_EXTENSION = ".zip";
-  public static final String DEFAULT_DEX_FILENAME = "classes.dex";
   public static final String JAVA_EXTENSION = ".java";
 
   public static boolean isDexFile(Path path) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 8145bb3..dd0a60c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -4,17 +4,23 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Resource.Origin;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.function.Function;
 
 public class InternalOptions {
@@ -118,11 +124,24 @@
   public ImmutableList<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
   public boolean minimalMainDex;
 
-  public String warningInvalidParameterAnnotations = null;
+  public static class InvalidParameterAnnotationInfo {
+    final DexMethod method;
+    final int expectedParameterCount;
+    final int actualParameterCount;
+
+    public InvalidParameterAnnotationInfo(
+        DexMethod method, int expectedParameterCount, int actualParameterCount) {
+      this.method = method;
+      this.expectedParameterCount = expectedParameterCount;
+      this.actualParameterCount = actualParameterCount;
+    }
+  }
 
   public boolean warningMissingEnclosingMember = false;
 
-  public int warningInvalidDebugInfoCount = 0;
+  private Map<Origin, List<InvalidParameterAnnotationInfo>> warningInvalidParameterAnnotations;
+
+  private Map<Origin, List<DexEncodedMethod>> warningInvalidDebugInfo;
 
   // Don't read code from dex files. Used to extract non-code information from vdex files where
   // the code contains unsupported byte codes.
@@ -132,23 +151,66 @@
 
   public DiagnosticsHandler diagnosticsHandler = new DefaultDiagnosticsHandler();
 
-  public void warningInvalidDebugInfo(DexEncodedMethod method, InvalidDebugInfoException e) {
-    warningInvalidDebugInfoCount++;
+  public void warningInvalidParameterAnnotations(
+      DexMethod method, Origin origin, int expected, int actual) {
+    if (warningInvalidParameterAnnotations == null) {
+      warningInvalidParameterAnnotations = new HashMap<>();
+    }
+    warningInvalidParameterAnnotations
+        .computeIfAbsent(origin, k -> new ArrayList<>())
+        .add(new InvalidParameterAnnotationInfo(method, expected, actual));
+  }
+
+  public void warningInvalidDebugInfo(
+      DexEncodedMethod method, Origin origin, InvalidDebugInfoException e) {
+    if (warningInvalidDebugInfo == null) {
+      warningInvalidDebugInfo = new HashMap<>();
+    }
+    warningInvalidDebugInfo.computeIfAbsent(origin, k -> new ArrayList<>()).add(method);
   }
 
   public boolean printWarnings() {
     boolean printed = false;
     boolean printOutdatedToolchain = false;
     if (warningInvalidParameterAnnotations != null) {
+      // TODO(b/67626202): Add a regression test with a program that hits this issue.
       diagnosticsHandler.warning(
-          new StringDiagnostic(warningInvalidParameterAnnotations));
+          new StringDiagnostic(
+              "Invalid parameter counts in MethodParameter attributes. "
+                  + "This is likely due to Proguard having removed a parameter."));
+      for (Origin origin : new TreeSet<>(warningInvalidParameterAnnotations.keySet())) {
+        StringBuilder builder =
+            new StringBuilder("Methods with invalid MethodParameter attributes:");
+        for (InvalidParameterAnnotationInfo info : warningInvalidParameterAnnotations.get(origin)) {
+          builder
+              .append("\n  ")
+              .append(info.method)
+              .append(" expected count: ")
+              .append(info.expectedParameterCount)
+              .append(" actual count: ")
+              .append(info.actualParameterCount);
+        }
+        diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString()));
+      }
       printed = true;
     }
-    if (warningInvalidDebugInfoCount > 0) {
+    if (warningInvalidDebugInfo != null) {
+      int count = 0;
+      for (List<DexEncodedMethod> methods : warningInvalidDebugInfo.values()) {
+        count += methods.size();
+      }
       diagnosticsHandler.warning(
-          new StringDiagnostic("Stripped invalid locals information from "
-          + warningInvalidDebugInfoCount
-          + (warningInvalidDebugInfoCount == 1 ? " method." : " methods.")));
+          new StringDiagnostic(
+              "Stripped invalid locals information from "
+                  + count
+                  + (count == 1 ? " method." : " methods.")));
+      for (Origin origin : new TreeSet<>(warningInvalidDebugInfo.keySet())) {
+        StringBuilder builder = new StringBuilder("Methods with invalid locals information:");
+        for (DexEncodedMethod method : warningInvalidDebugInfo.get(origin)) {
+          builder.append("\n  ").append(method.toSourceString());
+        }
+        diagnosticsHandler.info(new StringDiagnostic(origin, builder.toString()));
+      }
       printed = true;
       printOutdatedToolchain = true;
     }
@@ -161,7 +223,7 @@
       printOutdatedToolchain = true;
     }
     if (printOutdatedToolchain) {
-      diagnosticsHandler.warning(
+      diagnosticsHandler.info(
           new StringDiagnostic(
               "Some warnings are typically a sign of using an outdated Java toolchain."
                   + " To fix, recompile the source with an updated toolchain."));
diff --git a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
index c94d7be..863cce0 100644
--- a/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/LibraryClassCollection.java
@@ -3,8 +3,6 @@
 // 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.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.DexLibraryClass;
@@ -19,11 +17,6 @@
 
   @Override
   DexLibraryClass resolveClassConflict(DexLibraryClass a, DexLibraryClass b) {
-    if (a.origin != Resource.Kind.CLASSFILE || b.origin != Resource.Kind.CLASSFILE) {
-      // We only support conflicts for classes both coming from jar files.
-      throw new CompilationError(
-          "Library type already present: " + a.type.toSourceString());
-    }
     if (Log.ENABLED) {
       Log.warn(DexApplication.class,
           "Class `%s` was specified twice as a library type.", a.type.toSourceString());
diff --git a/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
index 51aa312..cc9c1e3 100644
--- a/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
+++ b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
@@ -14,8 +14,8 @@
   private byte[] bytes;
   private final Set<String> classDescriptors;
 
-  OneShotByteResource(Kind kind, byte[] bytes, Set<String> classDescriptors) {
-    super(kind);
+  OneShotByteResource(Origin origin, byte[] bytes, Set<String> classDescriptors) {
+    super(origin);
     assert bytes != null;
     this.bytes = bytes;
     this.classDescriptors = classDescriptors;
diff --git a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
index 3f402d0..01ea7ba 100644
--- a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.ClassFileResourceProvider;
 import com.android.tools.r8.Resource;
+import com.android.tools.r8.Resource.Origin;
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.HashMap;
@@ -16,6 +17,20 @@
  */
 public final class PreloadedClassFileProvider implements ClassFileResourceProvider {
 
+  private static class ClassDescriptorOrigin extends Origin {
+    private final String descriptor;
+
+    public ClassDescriptorOrigin(String descriptor) {
+      super(Origin.unknown());
+      this.descriptor = descriptor;
+    }
+
+    @Override
+    public String part() {
+      return descriptor;
+    }
+  }
+
   private final Map<String, byte[]> content;
 
   private PreloadedClassFileProvider(Map<String, byte[]> content) {
@@ -33,7 +48,8 @@
     if (bytes == null) {
       return null;
     }
-    return Resource.fromBytes(Resource.Kind.CLASSFILE, bytes, Collections.singleton(descriptor));
+    return Resource.fromBytes(
+        new ClassDescriptorOrigin(descriptor), bytes, Collections.singleton(descriptor));
   }
 
   public static ClassFileResourceProvider fromClassData(String descriptor, byte[] data) {
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 590ba83..17ca1a9 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -3,7 +3,6 @@
 // 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.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -50,8 +49,8 @@
 
   private static DexProgramClass resolveClassConflictImpl(DexProgramClass a, DexProgramClass b) {
     // Currently only allow collapsing synthetic lambda classes.
-    if (a.getOrigin() == Resource.Kind.DEX
-        && b.getOrigin() == Resource.Kind.DEX
+    if (a.originatesFromDexResource()
+        && b.originatesFromDexResource()
         && a.accessFlags.isSynthetic()
         && b.accessFlags.isSynthetic()
         && LambdaRewriter.hasLambdaClassPrefix(a.type)
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
index f33bf77..2cce6da 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
@@ -8,6 +8,8 @@
 import static com.android.tools.r8.utils.FileUtils.isDexFile;
 
 import com.android.tools.r8.Resource;
+import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.Resource.PathOrigin;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.google.common.io.ByteStreams;
@@ -26,12 +28,14 @@
 
 public class ProgramFileArchiveReader {
 
+  private final Origin origin;
   private final FilteredClassPath archive;
   private boolean ignoreDexInArchive;
   private List<Resource> dexResources = null;
   private List<Resource> classResources = null;
 
   ProgramFileArchiveReader(FilteredClassPath archive, boolean ignoreDexInArchive) {
+    origin = new PathOrigin(archive.getPath(), Origin.root());
     this.archive = archive;
     this.ignoreDexInArchive = ignoreDexInArchive;
   }
@@ -46,18 +50,23 @@
         ZipEntry entry = entries.nextElement();
         try (InputStream stream = zipFile.getInputStream(entry)) {
           Path name = Paths.get(entry.getName());
+          Origin entryOrigin = new PathOrigin(name, origin);
           if (archive.matchesFile(name)) {
             if (isDexFile(name)) {
               if (!ignoreDexInArchive) {
                 Resource resource =
-                    new OneShotByteResource(Resource.Kind.DEX, ByteStreams.toByteArray(stream),
+                    new OneShotByteResource(
+                        entryOrigin,
+                        ByteStreams.toByteArray(stream),
                         null);
                 dexResources.add(resource);
               }
             } else if (isClassFile(name)) {
               String descriptor = DescriptorUtils.guessTypeDescriptor(name);
-              Resource resource = new OneShotByteResource(Resource.Kind.CLASSFILE,
-                  ByteStreams.toByteArray(stream), Collections.singleton(descriptor));
+              Resource resource = new OneShotByteResource(
+                  entryOrigin,
+                  ByteStreams.toByteArray(stream),
+                  Collections.singleton(descriptor));
               classResources.add(resource);
             }
           }
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramResource.java b/src/main/java/com/android/tools/r8/utils/ProgramResource.java
new file mode 100644
index 0000000..0cbf11e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ProgramResource.java
@@ -0,0 +1,25 @@
+// 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;
+
+// A program resource can come from either DEX or Java-bytecode source. Currently we only support
+// Java-bytecode sources for classpath and library inputs. The occurance of VDEX here is to allow
+// toolings to read in VDEX sources (eg, the Marker extraction tool).
+public class ProgramResource {
+  public enum Kind {
+    DEX,
+    CLASS,
+    VDEX
+  }
+
+  public final Kind kind;
+  public final Resource resource;
+
+  ProgramResource(Kind kind, Resource resource) {
+    this.kind = kind;
+    this.resource = resource;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
index 9c690ff..b2add4a 100644
--- a/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/utils/StringDiagnostic.java
@@ -4,15 +4,27 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Resource.Origin;
 
 public class StringDiagnostic implements Diagnostic {
+  private final Origin origin;
   private final String message;
 
   public StringDiagnostic(String message) {
+    this(Origin.unknown(), message);
+  }
+
+  public StringDiagnostic(Origin origin, String message) {
+    this.origin = origin;
     this.message = message;
   }
 
   @Override
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  @Override
   public String getDiagnosticMessage() {
     return message;
   }
diff --git a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
index 239e4fd..f0c882f 100644
--- a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
@@ -16,7 +16,6 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
-
 public class R8CodeCanonicalizationTest {
 
   private static final String SOURCE_DEX = "invokeempty/classes.dex";
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index a03ae3d..e2cb5d6 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -40,7 +40,7 @@
   public void testRun1Dir() throws Exception {
     Path out = temp.newFolder("outdex").toPath();
     R8.run(getCommand(out));
-    Assert.assertTrue(Files.isRegularFile(out.resolve(FileUtils.DEFAULT_DEX_FILENAME)));
+    Assert.assertTrue(Files.isRegularFile(out.resolve(ToolHelper.DEFAULT_DEX_FILENAME)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(MAPPING)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
@@ -63,7 +63,7 @@
     } finally {
       executor.shutdown();
     }
-    Assert.assertTrue(Files.isRegularFile(out.resolve(FileUtils.DEFAULT_DEX_FILENAME)));
+    Assert.assertTrue(Files.isRegularFile(out.resolve(ToolHelper.DEFAULT_DEX_FILENAME)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(MAPPING)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
@@ -92,7 +92,7 @@
         "--pg-conf", testFlags.toString(),
         INPUT_JAR.toString());
     Assert.assertEquals(0, r8.exitCode);
-    Assert.assertTrue(Files.isRegularFile(out.resolve(FileUtils.DEFAULT_DEX_FILENAME)));
+    Assert.assertTrue(Files.isRegularFile(out.resolve(ToolHelper.DEFAULT_DEX_FILENAME)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(MAPPING)));
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 1fb3175..92de268 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -123,10 +123,10 @@
           // Interface initializer is not triggered after desugaring.
           .put("962-iface-static", AndroidApiLevel.N.getLevel())
           // Interface initializer is not triggered after desugaring.
-          .put("964-default-iface-init-gen",AndroidApiLevel.N.getLevel())
+          .put("964-default-iface-init-gen", AndroidApiLevel.N.getLevel())
           // AbstractMethodError (for method not implemented in class) instead of
           // IncompatibleClassChangeError (for conflict of default interface methods).
-          .put("968-default-partial-compile-gen",AndroidApiLevel.N.getLevel())
+          .put("968-default-partial-compile-gen", AndroidApiLevel.N.getLevel())
           // NoClassDefFoundError (for companion class) instead of NoSuchMethodError.
           .put("970-iface-super-resolution-gen", AndroidApiLevel.N.getLevel())
           // NoClassDefFoundError (for companion class) instead of AbstractMethodError.
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 5c7d303..e21e1bb 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.JarBuilder;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -184,7 +183,7 @@
   }
 
   private Path getOriginalDexFile() {
-    return Paths.get(EXAMPLE_DIR, pkg, FileUtils.DEFAULT_DEX_FILENAME);
+    return Paths.get(EXAMPLE_DIR, pkg, ToolHelper.DEFAULT_DEX_FILENAME);
   }
 
   private DexTool getTool() {
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index 3258b87..0f1a7bd 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -7,7 +7,6 @@
 
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 08f196c..fc4eefe 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -10,15 +10,14 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
 import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
 import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
@@ -405,7 +404,7 @@
       throws ZipException, IOException, ExecutionException {
     try (ZipFile zipFile = new ZipFile(zip.toFile())) {
       try (InputStream in =
-          zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+          zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
         return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
       }
     }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index 06916a8..4ff65ba 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -16,8 +16,6 @@
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
 import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
-import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
-import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
@@ -39,7 +37,6 @@
 import java.util.stream.Collectors;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
-import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -264,7 +261,7 @@
       throws ZipException, IOException, ExecutionException {
     try (ZipFile zipFile = new ZipFile(zip.toFile())) {
       try (InputStream in =
-          zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+          zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
         return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
       }
     }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index fdd2fa6..80c1e76 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -68,6 +68,7 @@
 
   public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
   public final static String PATH_SEPARATOR = File.pathSeparator;
+  public static final String DEFAULT_DEX_FILENAME = "classes.dex";
 
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final int DEFAULT_MIN_SDK = AndroidApiLevel.I.getLevel();
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 508ed3f..158388a 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.dex;
 
 import com.android.tools.r8.Resource;
-import com.android.tools.r8.Resource.Kind;
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.ReturnVoid;
@@ -83,10 +82,19 @@
       Collection<DexProgramClass> synthesizedFrom) {
     String desc = DescriptorUtils.javaTypeToDescriptor(name);
     DexType type = dexItemFactory.createType(desc);
-    return new DexProgramClass(type, Kind.DEX, new DexAccessFlags(Constants.ACC_PUBLIC),
-        dexItemFactory.objectType, DexTypeList.empty(), null, DexAnnotationSet.empty(),
-        DexEncodedField.EMPTY_ARRAY, DexEncodedField.EMPTY_ARRAY, DexEncodedMethod.EMPTY_ARRAY,
-        new DexEncodedMethod[]{makeMethod(type, stringCount, startOffset)},
+    return new DexProgramClass(
+        type,
+        null,
+        null,
+        new DexAccessFlags(Constants.ACC_PUBLIC),
+        dexItemFactory.objectType,
+        DexTypeList.empty(),
+        null,
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedMethod.EMPTY_ARRAY,
+        new DexEncodedMethod[] {makeMethod(type, stringCount, startOffset)},
         synthesizedFrom);
   }
 
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 0db6991..663901f 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.jasmin;
 
+import com.android.tools.r8.Resource.Origin;
+import com.android.tools.r8.Resource.PathOrigin;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -17,6 +19,7 @@
 import jasmin.ClassFile;
 import java.io.ByteArrayOutputStream;
 import java.io.StringReader;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -38,6 +41,10 @@
       this.superName = superName;
     }
 
+    public String getSourceFile() {
+      return name + ".j";
+    }
+
     public MethodSignature addVirtualMethod(
         String name,
         List<String> argumentTypes,
@@ -109,7 +116,7 @@
     @Override
     public String toString() {
       StringBuilder builder = new StringBuilder();
-      builder.append(".source ").append(name).append(".j\n");
+      builder.append(".source ").append(getSourceFile()).append("\n");
       builder.append(".class public ").append(name).append("\n");
       builder.append(".super ").append(superName).append("\n");
       if (makeInit) {
@@ -169,8 +176,17 @@
   }
 
   public AndroidApp build() throws Exception {
+    Origin root = new PathOrigin(Paths.get("JasminBuilder"), Origin.root());
     AndroidApp.Builder builder = AndroidApp.builder();
-    builder.addClassProgramData(buildClasses());
+    for (ClassBuilder clazz : classes) {
+      Origin origin = new Origin(root) {
+        @Override
+        public String part() {
+          return clazz.getSourceFile();
+        }
+      };
+      builder.addClassProgramData(origin, compile(clazz));
+    }
     return builder.build();
   }
 
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 c289c8e..b68f901 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -317,7 +317,7 @@
       byte[] ref = null;
       byte[] ref2 = null;
       for (Path testDir : testDirs.keySet()) {
-        Path primaryDexFile = testDir.resolve(FileUtils.DEFAULT_DEX_FILENAME);
+        Path primaryDexFile = testDir.resolve(ToolHelper.DEFAULT_DEX_FILENAME);
         Path secondaryDexFile = testDir.resolve("classes2.dex");
         assertTrue(Files.exists(primaryDexFile));
         boolean hasSecondaryDexFile = !allClasses && mode == CompilationMode.DEBUG;
@@ -600,6 +600,7 @@
           new DexProgramClass(
               type,
               null,
+              null,
               new DexAccessFlags(),
               factory.objectType,
               DexTypeList.empty(),
diff --git a/tools/archive.py b/tools/archive.py
index 4e36724..2ab1f1f 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -3,6 +3,7 @@
 # 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.
 
+import gradle
 import d8
 import os
 import r8
@@ -68,7 +69,10 @@
     print 'On master, using git hash for archiving'
     version = GetGitHash()
 
-  for jar in [utils.D8_JAR, utils.R8_JAR, utils.COMPATDX_JAR]:
+  # 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))
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index ee8981b..d8fac83 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -150,8 +150,6 @@
     args.extend(['--min-api', values['min-api']])
 
   if options.compiler == 'r8':
-    if 'pgmap' in values:
-      args.extend(['--pg-map', values['pgmap']])
     if 'pgconf' in values and not options.k:
       for pgconf in values['pgconf']:
         args.extend(['--pg-conf', pgconf])
diff --git a/tools/utils.py b/tools/utils.py
index 18f9779..7d198fa 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -18,11 +18,17 @@
 DEX_SEGMENTS_JAR = os.path.join(REPO_ROOT, 'build', 'libs',
     'dexsegments.jar')
 DEX_SEGMENTS_RESULT_PATTERN = re.compile('- ([^:]+): ([0-9]+)')
-COMPATDX_JAR = os.path.join(REPO_ROOT, 'build', 'libs', 'compatdx.jar')
 LIBS = os.path.join(REPO_ROOT, 'build', 'libs')
+
+D8 = 'd8'
+R8 = 'r8'
+COMPATDX = 'compatdx'
+COMPATPROGUARD = 'compatproguard'
+
 D8_JAR = os.path.join(LIBS, 'd8.jar')
 R8_JAR = os.path.join(LIBS, 'r8.jar')
 COMPATDX_JAR = os.path.join(LIBS, 'compatdx.jar')
+COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')
 
 def PrintCmd(s):
   if type(s) is list: