Merge "Don't check the current line when popping an activation frame."
diff --git a/LIBRARY-LICENSE b/LIBRARY-LICENSE
index 12674e3..bc69c40 100644
--- a/LIBRARY-LICENSE
+++ b/LIBRARY-LICENSE
@@ -48,3 +48,21 @@
   license: ASM license
   licenseUrl: http://asm.ow2.org/license.html
   url: http://asm.ow2.org/index.html
+- artifact: org.jetbrains.kotlin:kotlin-stdlib:+
+  name: org.jetbrains.kotlin:kotlin-stdlib
+  copyrightHolder: JetBrains s.r.o.
+  license: The Apache License, Version 2.0
+  licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
+  url: https://kotlinlang.org/
+- artifact: org.jetbrains.kotlinx:kotlinx-metadata-jvm:+
+  name: org.jetbrains.kotlinx:kotlinx-metadata-jvm
+  copyrightHolder: JetBrains s.r.o.
+  license: The Apache License, Version 2.0
+  licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
+  url: https://kotlinlang.org/
+- artifact: org.jetbrains:annotations:+
+  name: IntelliJ IDEA Annotations
+  copyrightHolder: JetBrains s.r.o.
+  license: The Apache Software License, Version 2.0
+  licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
+  url: http://www.jetbrains.org
diff --git a/build.gradle b/build.gradle
index 92d7471..7f41caa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,6 +37,7 @@
     gsonVersion = '2.7'
     junitVersion = '4.12'
     kotlinVersion = '1.2.30'
+    kotlinExtMetadataJVMVersion = '0.0.2'
     protobufVersion = '3.0.0'
     smaliVersion = '2.2b4'
 }
@@ -74,6 +75,7 @@
 
 repositories {
     maven { url 'https://maven.google.com' }
+    maven { url 'https://kotlin.bintray.com/kotlinx' }
     mavenCentral()
 }
 
@@ -223,6 +225,7 @@
         exclude group: 'org.codehaus.mojo'
     })
     compile group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion
+    compile "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion"
     compile group: 'org.ow2.asm', name: 'asm', version: asmVersion
     compile group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion
     compile group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 86ff5a5..9fcca89 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -531,7 +531,8 @@
   InternalOptions getInternalOptions() {
     InternalOptions internal = new InternalOptions(proguardConfiguration, getReporter());
     assert !internal.debug;
-    internal.debug = getMode() == CompilationMode.DEBUG;
+    internal.debug = getMode() == CompilationMode.DEBUG
+        || (forceProguardCompatibility && !proguardConfiguration.isObfuscating());
     internal.programConsumer = getProgramConsumer();
     internal.minApiLevel = getMinApiLevel();
     internal.enableDesugaring = getEnableDesugaring();
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index b775fd3..f896eee 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.3.0-dev";
+  public static final String LABEL = "1.3.1-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 026c2b5..d234702 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -155,6 +155,9 @@
     return isVirtualMethod() && !accessFlags.isAbstract();
   }
 
+  public boolean isPublicMethod() {
+    return accessFlags.isPublic();
+  }
 
   public boolean isPrivateMethod() {
     return accessFlags.isPrivate();
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 73c5118..76a80d4 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
@@ -66,7 +66,7 @@
   // Public for testing.
   public static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
   public static final String DEFAULT_METHOD_PREFIX = "$default$";
-  private static final String PRIVATE_METHOD_PREFIX = "$private$";
+  public static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final IRConverter converter;
   private final InternalOptions options;
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 1e8d57a..f4a76e6 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
@@ -91,7 +91,7 @@
     remainingMethods.clear();
 
     // Process static and private methods, move them into companion class as well,
-    // make private instance methods static.
+    // make private instance methods public static.
     for (DexEncodedMethod direct : iface.directMethods()) {
       MethodAccessFlags originalFlags = direct.accessFlags;
       MethodAccessFlags newFlags = originalFlags.copy();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index ae57f48..e610d20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -762,6 +762,11 @@
       return;
     }
 
+    DexClass clazz = appInfo.definitionFor(method.method.holder);
+    if (clazz == null) {
+      return;
+    }
+
     boolean receiverUsedAsReturnValue = false;
     boolean seenSuperInitCall = false;
     for (Instruction insn : receiver.uniqueUsers()) {
@@ -773,7 +778,7 @@
       if (insn.isInstanceGet() ||
           (insn.isInstancePut() && insn.asInstancePut().object() == receiver)) {
         DexField field = insn.asFieldInstruction().getField();
-        if (field.clazz == method.method.holder) {
+        if (field.clazz == clazz.type && clazz.lookupInstanceField(field) != null) {
           // Since class inliner currently only supports classes directly extending
           // java.lang.Object, we don't need to worry about fields defined in superclasses.
           continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 7bdff33..d7a57df 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -169,14 +169,16 @@
 
     Map<InvokeMethodWithReceiver, InliningInfo> methodCalls = new IdentityHashMap<>();
 
+    DexClass definition = appInfo.definitionFor(clazz);
+
     for (Instruction user : receiver.uniqueUsers()) {
       // Field read/write.
       if (user.isInstanceGet() ||
           (user.isInstancePut() && user.asInstancePut().value() != receiver)) {
-        if (user.asFieldInstruction().getField().clazz == newInstanceInsn.clazz) {
-          // Eligible field read or write. Note: as long as we consider only classes eligible
-          // if they directly extend java.lang.Object we don't need to check if the field
-          // really exists in the class.
+        DexField field = user.asFieldInstruction().getField();
+        if (field.clazz == newInstanceInsn.clazz && definition.lookupInstanceField(field) != null) {
+          // Since class inliner currently only supports classes directly extending
+          // java.lang.Object, we don't need to worry about fields defined in superclasses.
           continue;
         }
         return null; // Not eligible.
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index cf2b2bc..0af41b1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -5,19 +5,11 @@
 package com.android.tools.r8.kotlin;
 
 import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
-import com.android.tools.r8.graph.DexValue.DexValueInt;
-import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.kotlin.KotlinSyntheticClass.Flavour;
-import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
@@ -58,10 +50,13 @@
 
     public final DexString kotlinStyleLambdaInstanceName = factory.createString("INSTANCE");
 
+    public final DexType functionBase = factory.createType("Lkotlin/jvm/internal/FunctionBase;");
     public final DexType lambdaType = factory.createType("Lkotlin/jvm/internal/Lambda;");
 
-    public final DexMethod lambdaInitializerMethod = factory.createMethod(lambdaType,
-        factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
+    public final DexMethod lambdaInitializerMethod = factory.createMethod(
+        lambdaType,
+        factory.createProto(factory.voidType, factory.intType),
+        factory.constructorMethodName);
 
     public boolean isFunctionInterface(DexType type) {
       return functions.contains(type);
@@ -70,9 +65,14 @@
 
   public final class Metadata {
     public final DexType kotlinMetadataType = factory.createType("Lkotlin/Metadata;");
-    public final DexString elementNameK = factory.createString("k");
-    public final DexString elementNameD1 = factory.createString("d1");
-    public final DexString elementNameD2 = factory.createString("d2");
+    public final DexString kind = factory.createString("k");
+    public final DexString metadataVersion = factory.createString("mv");
+    public final DexString bytecodeVersion = factory.createString("bv");
+    public final DexString data1 = factory.createString("d1");
+    public final DexString data2 = factory.createString("d2");
+    public final DexString extraString = factory.createString("xs");
+    public final DexString packageName = factory.createString("pn");
+    public final DexString extraInt = factory.createString("xi");
   }
 
   // kotlin.jvm.internal.Intrinsics class
@@ -86,98 +86,6 @@
 
   // Calculates kotlin info for a class.
   public KotlinInfo getKotlinInfo(DexClass clazz, DiagnosticsHandler reporter) {
-    if (clazz.annotations.isEmpty()) {
-      return null;
-    }
-    DexAnnotation meta = clazz.annotations.getFirstMatching(metadata.kotlinMetadataType);
-    if (meta != null) {
-      try {
-        return createKotlinInfo(clazz, meta);
-      } catch (MetadataError e) {
-        reporter.warning(
-            new StringDiagnostic("Class " + clazz.type.toSourceString() +
-                " has malformed kotlin.Metadata: " + e.getMessage()));
-      }
-    }
-    return null;
-  }
-
-  private KotlinInfo createKotlinInfo(DexClass clazz, DexAnnotation meta) {
-    DexAnnotationElement kindElement = getAnnotationElement(meta, metadata.elementNameK);
-    if (kindElement == null) {
-      throw new MetadataError("element 'k' is missing");
-    }
-
-    DexValue value = kindElement.value;
-    if (!(value instanceof DexValueInt)) {
-      throw new MetadataError("invalid 'k' value: " + value.toSourceString());
-    }
-
-    DexValueInt intValue = (DexValueInt) value;
-    switch (intValue.value) {
-      case 1:
-        return new KotlinClass();
-      case 2:
-        return new KotlinFile();
-      case 3:
-        return createSyntheticClass(clazz, meta);
-      case 4:
-        return new KotlinClassFacade();
-      case 5:
-        return new KotlinClassPart();
-      default:
-        throw new MetadataError("unsupported 'k' value: " + value.toSourceString());
-    }
-  }
-
-  private KotlinSyntheticClass createSyntheticClass(DexClass clazz, DexAnnotation meta) {
-    if (isKotlinStyleLambda(clazz)) {
-      return new KotlinSyntheticClass(Flavour.KotlinStyleLambda);
-    }
-    if (isJavaStyleLambda(clazz, meta)) {
-      return new KotlinSyntheticClass(Flavour.JavaStyleLambda);
-    }
-    return new KotlinSyntheticClass(Flavour.Unclassified);
-  }
-
-  private boolean isKotlinStyleLambda(DexClass clazz) {
-    // TODO: replace with direct hints from kotlin metadata when available.
-    return clazz.superType == this.functional.lambdaType;
-  }
-
-  private boolean isJavaStyleLambda(DexClass clazz, DexAnnotation meta) {
-    assert !isKotlinStyleLambda(clazz);
-    return clazz.superType == this.factory.objectType &&
-        clazz.interfaces.size() == 1 &&
-        isAnnotationElementNotEmpty(meta, metadata.elementNameD1);
-  }
-
-  private DexAnnotationElement getAnnotationElement(DexAnnotation annotation, DexString name) {
-    for (DexAnnotationElement element : annotation.annotation.elements) {
-      if (element.name == name) {
-        return element;
-      }
-    }
-    return null;
-  }
-
-  private boolean isAnnotationElementNotEmpty(DexAnnotation annotation, DexString name) {
-    for (DexAnnotationElement element : annotation.annotation.elements) {
-      if (element.name == name && element.value instanceof DexValueArray) {
-        DexValue[] values = ((DexValueArray) element.value).getValues();
-        if (values.length == 1 && values[0] instanceof DexValueString) {
-          return true;
-        }
-        // Must be broken metadata.
-        assert false;
-      }
-    }
-    return false;
-  }
-
-  private static class MetadataError extends RuntimeException {
-    MetadataError(String cause) {
-      super(cause);
-    }
+    return KotlinClassMetadataReader.getKotlinInfo(this, clazz, reporter);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
index fc81994..74184a5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public class KotlinClass extends KotlinInfo {
+import kotlinx.metadata.KmClassVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public class KotlinClass extends KotlinInfo<KotlinClassMetadata.Class> {
+
+  static KotlinClass fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.Class;
+    KotlinClassMetadata.Class kClass = (KotlinClassMetadata.Class) kotlinClassMetadata;
+    return new KotlinClass(kClass);
+  }
+
+  private KotlinClass(KotlinClassMetadata.Class metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.Class metadata) {
+    ClassMetadataVisitor visitor = new ClassMetadataVisitor();
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(visitor);
+  }
+
+  private static class ClassMetadataVisitor extends KmClassVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Class;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinClass() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
index e829a27..62db3d1 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
@@ -4,7 +4,26 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinClassFacade extends KotlinInfo {
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinClassFacade extends KotlinInfo<KotlinClassMetadata.MultiFileClassFacade> {
+
+  static KotlinClassFacade fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassFacade;
+    KotlinClassMetadata.MultiFileClassFacade multiFileClassFacade =
+        (KotlinClassMetadata.MultiFileClassFacade) kotlinClassMetadata;
+    return new KotlinClassFacade(multiFileClassFacade);
+  }
+
+  private KotlinClassFacade(KotlinClassMetadata.MultiFileClassFacade metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.MultiFileClassFacade metadata) {
+    // No worries about lazy parsing/verifying, since no API to explore metadata details.
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Facade;
@@ -20,7 +39,4 @@
     return this;
   }
 
-  KotlinClassFacade() {
-    super();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
new file mode 100644
index 0000000..32aebff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2018, 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.kotlin;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import kotlinx.metadata.InconsistentKotlinMetadataException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+final class KotlinClassMetadataReader {
+
+  static KotlinInfo getKotlinInfo(
+      Kotlin kotlin,
+      DexClass clazz,
+      DiagnosticsHandler reporter) {
+    if (clazz.annotations.isEmpty()) {
+      return null;
+    }
+    DexAnnotation meta = clazz.annotations.getFirstMatching(kotlin.metadata.kotlinMetadataType);
+    if (meta != null) {
+      try {
+        return createKotlinInfo(kotlin, clazz, meta);
+      } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
+        reporter.warning(
+            new StringDiagnostic("Class " + clazz.type.toSourceString()
+                + " has malformed kotlin.Metadata: " + e.getMessage()));
+      } catch (Throwable e) {
+         reporter.warning(
+            new StringDiagnostic("Unexpected error while reading " + clazz.type.toSourceString()
+                + "'s kotlin.Metadata: " + e.getMessage()));
+      }
+    }
+    return null;
+  }
+
+  private static KotlinInfo createKotlinInfo(
+      Kotlin kotlin,
+      DexClass clazz,
+      DexAnnotation meta) {
+    Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
+    for (DexAnnotationElement element : meta.annotation.elements) {
+      elementMap.put(element.name, element);
+    }
+
+    DexAnnotationElement kind = elementMap.get(kotlin.metadata.kind);
+    if (kind == null) {
+      throw new MetadataError("element 'k' is missing.");
+    }
+    Integer k = (Integer) kind.value.getBoxedValue();
+    DexAnnotationElement metadataVersion = elementMap.get(kotlin.metadata.metadataVersion);
+    int[] mv = metadataVersion == null ? null : getUnboxedIntArray(metadataVersion.value, "mv");
+    DexAnnotationElement bytecodeVersion = elementMap.get(kotlin.metadata.bytecodeVersion);
+    int[] bv = bytecodeVersion == null ? null : getUnboxedIntArray(bytecodeVersion.value, "bv");
+    DexAnnotationElement data1 = elementMap.get(kotlin.metadata.data1);
+    String[] d1 = data1 == null ? null : getUnboxedStringArray(data1.value, "d1");
+    DexAnnotationElement data2 = elementMap.get(kotlin.metadata.data2);
+    String[] d2 = data2 == null ? null : getUnboxedStringArray(data2.value, "d2");
+    DexAnnotationElement extraString = elementMap.get(kotlin.metadata.extraString);
+    String xs = extraString == null ? null : getUnboxedString(extraString.value, "xs");
+    DexAnnotationElement packageName = elementMap.get(kotlin.metadata.packageName);
+    String pn = packageName == null ? null : getUnboxedString(packageName.value, "pn");
+    DexAnnotationElement extraInt = elementMap.get(kotlin.metadata.extraInt);
+    Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue();
+
+    KotlinClassHeader header = new KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi);
+    KotlinClassMetadata kMetadata = KotlinClassMetadata.read(header);
+
+    if (kMetadata instanceof KotlinClassMetadata.Class) {
+      return KotlinClass.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
+      return KotlinFile.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
+      return KotlinClassFacade.fromKotlinClassMetadata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
+      return KotlinClassPart.fromKotlinClassMetdata(kMetadata);
+    } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
+      return KotlinSyntheticClass.fromKotlinClassMetadata(kMetadata, kotlin, clazz);
+    } else {
+      throw new MetadataError("unsupported 'k' value: " + k);
+    }
+  }
+
+  private static int[] getUnboxedIntArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray intArrayValue = (DexValueArray) v;
+    DexValue[] values = intArrayValue.getValues();
+    int[] result = new int [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = (Integer) values[i].getBoxedValue();
+    }
+    return result;
+  }
+
+  private static String[] getUnboxedStringArray(DexValue v, String elementName) {
+    if (!(v instanceof DexValueArray)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    DexValueArray stringArrayValue = (DexValueArray) v;
+    DexValue[] values = stringArrayValue.getValues();
+    String[] result = new String [values.length];
+    for (int i = 0; i < values.length; i++) {
+      result[i] = getUnboxedString(values[i], elementName + "[" + i + "]");
+    }
+    return result;
+  }
+
+  private static String getUnboxedString(DexValue v, String elementName) {
+    if (!(v instanceof DexValueString)) {
+      throw new MetadataError("invalid '" + elementName + "' value: " + v.toSourceString());
+    }
+    return ((DexValueString) v).getValue().toString();
+  }
+
+  private static class MetadataError extends RuntimeException {
+    MetadataError(String cause) {
+      super(cause);
+    }
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
index d6da817..da66e6c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinClassPart extends KotlinInfo {
+import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> {
+
+  static KotlinClassPart fromKotlinClassMetdata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassPart;
+    KotlinClassMetadata.MultiFileClassPart multiFileClassPart =
+        (KotlinClassMetadata.MultiFileClassPart) kotlinClassMetadata;
+    return new KotlinClassPart(multiFileClassPart);
+  }
+
+  private KotlinClassPart(KotlinClassMetadata.MultiFileClassPart metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.MultiFileClassPart metadata) {
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(new MultiFileClassPartMetadataVisitor());
+  }
+
+  private static class MultiFileClassPartMetadataVisitor extends KmPackageVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.Part;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinClassPart() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
index 38a77ad..bcb70ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
@@ -4,7 +4,31 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinFile extends KotlinInfo {
+import kotlinx.metadata.KmPackageVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinFile extends KotlinInfo<KotlinClassMetadata.FileFacade> {
+
+  static KotlinFile fromKotlinClassMetadata(KotlinClassMetadata kotlinClassMetadata) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.FileFacade;
+    KotlinClassMetadata.FileFacade fileFacade =
+        (KotlinClassMetadata.FileFacade) kotlinClassMetadata;
+    return new KotlinFile(fileFacade);
+  }
+
+  private KotlinFile(KotlinClassMetadata.FileFacade metadata) {
+    super(metadata);
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.FileFacade metadata) {
+    // To avoid lazy parsing/verifying metadata.
+    metadata.accept(new FileFacadeMetadataVisitor());
+  }
+
+  private static class FileFacadeMetadataVisitor extends KmPackageVisitor {
+  }
+
   @Override
   public Kind getKind() {
     return Kind.File;
@@ -20,6 +44,4 @@
     return this;
   }
 
-  KotlinFile() {
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
index 4043bc6..702e0eb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
@@ -4,11 +4,23 @@
 
 package com.android.tools.r8.kotlin;
 
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
 // Provides access to kotlin information.
-public abstract class KotlinInfo {
+public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
+  MetadataKind metadata;
+
   KotlinInfo() {
   }
 
+  KotlinInfo(MetadataKind metadata) {
+    validateMetadata(metadata);
+    this.metadata = metadata;
+  }
+
+  // Subtypes will define how to validate the given metadata.
+  abstract void validateMetadata(MetadataKind metadata);
+
   public enum Kind {
     Class, File, Synthetic, Part, Facade
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
index 68721bb..4660800 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
@@ -4,7 +4,11 @@
 
 package com.android.tools.r8.kotlin;
 
-public final class KotlinSyntheticClass extends KotlinInfo {
+import com.android.tools.r8.graph.DexClass;
+import kotlinx.metadata.KmLambdaVisitor;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public final class KotlinSyntheticClass extends KotlinInfo<KotlinClassMetadata.SyntheticClass> {
   public enum Flavour {
     KotlinStyleLambda,
     JavaStyleLambda,
@@ -13,12 +17,40 @@
 
   private final Flavour flavour;
 
-  KotlinSyntheticClass(Flavour flavour) {
+  static KotlinSyntheticClass fromKotlinClassMetadata(
+      KotlinClassMetadata kotlinClassMetadata, Kotlin kotlin, DexClass clazz) {
+    assert kotlinClassMetadata instanceof KotlinClassMetadata.SyntheticClass;
+    KotlinClassMetadata.SyntheticClass syntheticClass =
+        (KotlinClassMetadata.SyntheticClass) kotlinClassMetadata;
+    if (isKotlinStyleLambda(syntheticClass, kotlin, clazz)) {
+      return new KotlinSyntheticClass(Flavour.KotlinStyleLambda, syntheticClass);
+    } else if (isJavaStyleLambda(syntheticClass, kotlin, clazz)) {
+      return new KotlinSyntheticClass(Flavour.JavaStyleLambda, syntheticClass);
+    } else {
+      return new KotlinSyntheticClass(Flavour.Unclassified, syntheticClass);
+    }
+  }
+
+  private KotlinSyntheticClass(Flavour flavour, KotlinClassMetadata.SyntheticClass metadata) {
     this.flavour = flavour;
+    validateMetadata(metadata);
+    this.metadata = metadata;
+  }
+
+  @Override
+  void validateMetadata(KotlinClassMetadata.SyntheticClass metadata) {
+    if (metadata.isLambda()) {
+      SyntheticClassMetadataVisitor visitor = new SyntheticClassMetadataVisitor();
+      // To avoid lazy parsing/verifying metadata.
+      metadata.accept(visitor);
+    }
+  }
+
+  private static class SyntheticClassMetadataVisitor extends KmLambdaVisitor {
   }
 
   public boolean isLambda() {
-    return flavour == Flavour.KotlinStyleLambda || flavour == Flavour.JavaStyleLambda;
+    return isKotlinStyleLambda() || isJavaStyleLambda();
   }
 
   public boolean isKotlinStyleLambda() {
@@ -43,4 +75,31 @@
   public KotlinSyntheticClass asSyntheticClass() {
     return this;
   }
+
+  /**
+   * Returns {@code true} if the given {@link DexClass} is a Kotlin-style lambda:
+   *   a class that
+   *     1) is recognized as lambda in its Kotlin metadata;
+   *     2) directly extends kotlin.jvm.internal.Lambda
+   */
+  private static boolean isKotlinStyleLambda(
+      KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
+    return metadata.isLambda()
+        && clazz.superType == kotlin.functional.lambdaType;
+  }
+
+  /**
+   * Returns {@code true} if the given {@link DexClass} is a Java-style lambda:
+   *   a class that
+   *     1) is recognized as lambda in its Kotlin metadata;
+   *     2) doesn't extend any other class;
+   *     3) directly implements only one Java SAM.
+   */
+  private static boolean isJavaStyleLambda(
+      KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
+    return metadata.isLambda()
+        && clazz.superType == kotlin.factory.objectType
+        && clazz.interfaces.size() == 1;
+  }
+
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 2b51720..dae98ad 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1651,7 +1651,7 @@
           rewriteKeysWhileMergingValues(previous.staticFieldWrites, lense::lookupField);
       this.fieldsRead = rewriteItems(previous.fieldsRead, lense::lookupField);
       this.fieldsWritten = rewriteItems(previous.fieldsWritten, lense::lookupField);
-      this.pinnedItems = rewriteMixedItems(previous.pinnedItems, lense);
+      this.pinnedItems = rewriteMixedItemsConservatively(previous.pinnedItems, lense);
       this.virtualInvokes = rewriteMethodsConservatively(previous.virtualInvokes, lense);
       this.interfaceInvokes = rewriteMethodsConservatively(previous.interfaceInvokes, lense);
       this.superInvokes = rewriteMethodsConservatively(previous.superInvokes, lense);
@@ -1665,7 +1665,8 @@
       this.assumedValues = previous.assumedValues;
       assert assertNotModifiedByLense(previous.alwaysInline, lense);
       this.alwaysInline = previous.alwaysInline;
-      this.identifierNameStrings = rewriteMixedItems(previous.identifierNameStrings, lense);
+      this.identifierNameStrings =
+          rewriteMixedItemsConservatively(previous.identifierNameStrings, lense);
       // Switchmap classes should never be affected by renaming.
       assert assertNotModifiedByLense(
           previous.switchMaps.keySet().stream().map(this::definitionFor).filter(Objects::nonNull)
@@ -1846,7 +1847,7 @@
       return Collections.unmodifiableMap(result);
     }
 
-    private static ImmutableSet<DexItem> rewriteMixedItems(
+    private static ImmutableSet<DexItem> rewriteMixedItemsConservatively(
         Set<DexItem> original, GraphLense lense) {
       ImmutableSet.Builder<DexItem> builder = ImmutableSet.builder();
       for (DexItem item : original) {
@@ -1854,7 +1855,12 @@
         if (item instanceof DexType) {
           builder.add(lense.lookupType((DexType) item));
         } else if (item instanceof DexMethod) {
-          builder.add(lense.lookupMethod((DexMethod) item));
+          DexMethod method = (DexMethod) item;
+          if (lense.isContextFreeForMethod(method)) {
+            builder.add(lense.lookupMethod(method));
+          } else {
+            builder.addAll(lense.lookupMethodInAllContexts(method));
+          }
         } else if (item instanceof DexField) {
           builder.add(lense.lookupField((DexField) item));
         } else {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 5da6f06..c737055 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
@@ -87,28 +88,20 @@
   // Returns a set of types that must not be merged into other types.
   private Set<DexType> getPinnedTypes(Iterable<DexProgramClass> classes) {
     Set<DexType> pinnedTypes = new HashSet<>();
+
+    // For all pinned fields, also pin the type of the field (because changing the type of the field
+    // implicitly changes the signature of the field). Similarly, for all pinned methods, also pin
+    // the return type and the parameter types of the method.
+    extractPinnedItems(appInfo.pinnedItems, pinnedTypes);
+
+    // TODO(christofferqa): Remove the invariant that the graph lense should not modify any
+    // methods from the sets alwaysInline and noSideEffects (see use of assertNotModifiedBy-
+    // Lense).
+    extractPinnedItems(appInfo.alwaysInline, pinnedTypes);
+    extractPinnedItems(appInfo.noSideEffects.keySet(), pinnedTypes);
+
     for (DexProgramClass clazz : classes) {
       for (DexEncodedMethod method : clazz.methods()) {
-        // TODO(christofferqa): Remove the invariant that the graph lense should not modify any
-        // methods from the sets alwaysInline and noSideEffects (see use of assertNotModifiedBy-
-        // Lense).
-        if (appInfo.alwaysInline.contains(method) || appInfo.noSideEffects.containsKey(method)) {
-          DexClass other = appInfo.definitionFor(method.method.proto.returnType);
-          if (other != null && other.isProgramClass()) {
-            // If we were to merge [other] into its sub class, then we would implicitly change the
-            // signature of this method, and therefore break the invariant.
-            pinnedTypes.add(other.type);
-          }
-          for (DexType parameterType : method.method.proto.parameters.values) {
-            other = appInfo.definitionFor(parameterType);
-            if (other != null && other.isProgramClass()) {
-              // If we were to merge [other] into its sub class, then we would implicitly change the
-              // signature of this method, and therefore break the invariant.
-              pinnedTypes.add(other.type);
-            }
-          }
-        }
-
         // Avoid merging two types if this could remove a NoSuchMethodError, as illustrated by the
         // following example. (Alternatively, it would be possible to merge A and B and rewrite the
         // "invoke-super A.m" instruction into "invoke-super Object.m" to preserve the error. This
@@ -143,6 +136,38 @@
     return pinnedTypes;
   }
 
+  private void extractPinnedItems(Iterable<DexItem> items, Set<DexType> pinnedTypes) {
+    for (DexItem item : items) {
+      // Note: Nothing to do for the case where item is a DexType, since we check for this case
+      // using appInfo.isPinned.
+      if (item instanceof DexField) {
+        // Pin the type of the field.
+        DexField field = (DexField) item;
+        DexClass clazz = appInfo.definitionFor(field.type);
+        if (clazz != null && clazz.isProgramClass()) {
+          pinnedTypes.add(clazz.type);
+        }
+      } else if (item instanceof DexMethod) {
+        // Pin the return type and the parameter types of the method.
+        DexMethod method = (DexMethod) item;
+        DexClass clazz = appInfo.definitionFor(method.proto.returnType);
+        if (clazz != null && clazz.isProgramClass()) {
+          // If we were to merge [other] into its sub class, then we would implicitly change the
+          // signature of this method, and therefore break the invariant.
+          pinnedTypes.add(clazz.type);
+        }
+        for (DexType parameterType : method.proto.parameters.values) {
+          clazz = appInfo.definitionFor(parameterType);
+          if (clazz != null && clazz.isProgramClass()) {
+            // If we were to merge [other] into its sub class, then we would implicitly change the
+            // signature of this method, and therefore break the invariant.
+            pinnedTypes.add(clazz.type);
+          }
+        }
+      }
+    }
+  }
+
   private boolean isMergeCandidate(DexProgramClass clazz, Set<DexType> pinnedTypes) {
     if (appInfo.instantiatedTypes.contains(clazz.type)
         || appInfo.isPinned(clazz.type)
@@ -320,6 +345,11 @@
         }
         continue;
       }
+      // Field resolution first considers the direct interfaces of [targetClass] before it proceeds
+      // to the super class.
+      if (fieldResolutionMayChange(clazz, targetClass)) {
+        continue;
+      }
       // Guard against the case where we have two methods that may get the same signature
       // if we replace types. This is rare, so we approximate and err on the safe side here.
       if (new CollisionDetector(clazz.type, targetClass.type, getInvokes(), mergedClasses)
@@ -365,6 +395,35 @@
     return renamedMembersLense.build(graphLense);
   }
 
+  private boolean fieldResolutionMayChange(DexClass source, DexClass target) {
+    if (source.type == target.superType) {
+      // If there is a "iget Target.f" or "iput Target.f" instruction in target, and the class
+      // Target implements an interface that declares a static final field f, this should yield an
+      // IncompatibleClassChangeError.
+      // TODO(christofferqa): In the following we only check if a static field from an interface
+      // shadows an instance field from [source]. We could actually check if there is an iget/iput
+      // instruction whose resolution would be affected by the merge. The situation where a static
+      // field shadows an instance field is probably not widespread in practice, though.
+      FieldSignatureEquivalence equivalence = FieldSignatureEquivalence.get();
+      Set<Wrapper<DexField>> staticFieldsInInterfacesOfTarget = new HashSet<>();
+      for (DexType interfaceType : target.interfaces.values) {
+        DexClass clazz = appInfo.definitionFor(interfaceType);
+        for (DexEncodedField staticField : clazz.staticFields()) {
+          staticFieldsInInterfacesOfTarget.add(equivalence.wrap(staticField.field));
+        }
+      }
+      for (DexEncodedField instanceField : source.instanceFields()) {
+        if (staticFieldsInInterfacesOfTarget.contains(equivalence.wrap(instanceField.field))) {
+          // An instruction "iget Target.f" or "iput Target.f" that used to hit a static field in an
+          // interface would now hit an instance field from [source], so that an IncompatibleClass-
+          // ChangeError would no longer be thrown. Abort merge.
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   private class ClassMerger {
 
     private static final String CONSTRUCTOR_NAME = "constructor";
@@ -398,6 +457,10 @@
           directMethods.add(renameConstructor(directMethod));
         } else {
           directMethods.add(directMethod);
+
+          if (!directMethod.isStaticMethod()) {
+            blockRedirectionOfSuperCalls(directMethod.method);
+          }
         }
       }
 
@@ -433,6 +496,7 @@
         // Record that invoke-super instructions in the target class should be redirected to the
         // newly created direct method.
         redirectSuperCallsInTarget(virtualMethod.method, resultingDirectMethod.method);
+        blockRedirectionOfSuperCalls(resultingDirectMethod.method);
 
         if (shadowedBy == null) {
           // In addition to the newly added direct method, create a virtual method such that we do
@@ -456,6 +520,7 @@
               (existing, method) -> {
                 DexEncodedMethod renamedMethod = renameMethod(method, target.type, true);
                 deferredRenamings.map(method.method, renamedMethod.method);
+                blockRedirectionOfSuperCalls(renamedMethod.method);
                 return renamedMethod;
               });
       Collection<DexEncodedMethod> mergedVirtualMethods =
@@ -544,6 +609,26 @@
       }
     }
 
+    private void blockRedirectionOfSuperCalls(DexMethod method) {
+      // We are merging a class B into C. The methods from B are being moved into C, and then we
+      // subsequently rewrite the invoke-super instructions in C that hit a method in B, such that
+      // they use an invoke-direct instruction instead. In this process, we need to avoid rewriting
+      // the invoke-super instructions that originally was in the superclass B.
+      //
+      // Example:
+      //   class A {
+      //     public void m() {}
+      //   }
+      //   class B extends A {
+      //     public void m() { super.m(); } <- invoke must not be rewritten to invoke-direct
+      //                                       (this would lead to an infinite loop)
+      //   }
+      //   class C extends B {
+      //     public void m() { super.m(); } <- invoke needs to be rewritten to invoke-direct
+      //   }
+      deferredRenamings.markMethodAsMerged(method);
+    }
+
     private boolean resolutionSucceeds(DexMethod targetMethod) {
       DexClass enclosingClass = appInfo.definitionFor(targetMethod.holder);
       return enclosingClass != null
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index ea57d1d..942a857 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -45,16 +45,19 @@
 
   private final Map<DexField, DexField> fieldMap;
   private final Map<DexMethod, DexMethod> methodMap;
+  private final Set<DexMethod> mergedMethods;
   private final Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps;
 
   public VerticalClassMergerGraphLense(
       Map<DexField, DexField> fieldMap,
       Map<DexMethod, DexMethod> methodMap,
+      Set<DexMethod> mergedMethods,
       Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps,
       GraphLense previousLense) {
     this.previousLense = previousLense;
     this.fieldMap = fieldMap;
     this.methodMap = methodMap;
+    this.mergedMethods = mergedMethods;
     this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
   }
 
@@ -67,7 +70,7 @@
   public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context, Type type) {
     assert isContextFreeForMethod(method) || (context != null && type != null);
     DexMethod previous = previousLense.lookupMethod(method, context, type);
-    if (type == Type.SUPER) {
+    if (type == Type.SUPER && !mergedMethods.contains(context.method)) {
       Map<DexMethod, DexMethod> virtualToDirectMethodMap =
           contextualVirtualToDirectMethodMaps.get(context.method.holder);
       if (virtualToDirectMethodMap != null) {
@@ -127,6 +130,7 @@
     private final ImmutableMap.Builder<DexField, DexField> fieldMapBuilder = ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, DexMethod> methodMapBuilder =
         ImmutableMap.builder();
+    private final ImmutableSet.Builder<DexMethod> mergedMethodsBuilder = ImmutableSet.builder();
     private final Map<DexType, Map<DexMethod, DexMethod>> contextualVirtualToDirectMethodMaps =
         new HashMap<>();
 
@@ -141,7 +145,15 @@
         return previousLense;
       }
       return new VerticalClassMergerGraphLense(
-          fieldMap, methodMap, contextualVirtualToDirectMethodMaps, previousLense);
+          fieldMap,
+          methodMap,
+          mergedMethodsBuilder.build(),
+          contextualVirtualToDirectMethodMaps,
+          previousLense);
+    }
+
+    public void markMethodAsMerged(DexMethod method) {
+      mergedMethodsBuilder.add(method);
     }
 
     public void map(DexField from, DexField to) {
@@ -161,6 +173,7 @@
     public void merge(VerticalClassMergerGraphLense.Builder builder) {
       fieldMapBuilder.putAll(builder.fieldMapBuilder.build());
       methodMapBuilder.putAll(builder.methodMapBuilder.build());
+      mergedMethodsBuilder.addAll(builder.mergedMethodsBuilder.build());
       for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) {
         Map<DexMethod, DexMethod> current = contextualVirtualToDirectMethodMaps.get(context);
         Map<DexMethod, DexMethod> other = builder.contextualVirtualToDirectMethodMaps.get(context);
diff --git a/src/test/examples/classmerging/RewritePinnedMethodTest.java b/src/test/examples/classmerging/RewritePinnedMethodTest.java
new file mode 100644
index 0000000..3d25805
--- /dev/null
+++ b/src/test/examples/classmerging/RewritePinnedMethodTest.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2018, 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 classmerging;
+
+public class RewritePinnedMethodTest {
+
+  public static void main(String[] args) {
+    C obj = new C();
+    obj.m();
+  }
+
+  // A.m is pinned by a keep rule, to prevent it from being merged into B.
+  public static class A {
+    public void m() {
+      System.out.println("In A.m");
+    }
+  }
+
+  // B is merged into C.
+  public static class B extends A {
+    @Override
+    public void m() {
+      System.out.println("In B.m");
+      super.m();
+    }
+  }
+
+  public static class C extends B {
+    @Override
+    public void m() {
+      System.out.println("In C.m");
+      // This invocation is changed from invoke-super to invoke-direct. It would be valid for this
+      // instruction to be on the form "invoke-super A.m". Therefore, the graph lense contains a
+      // mapping for "A.m" from (context: "C.m", type: SUPER) to C.m$B, where the method C.m$B is
+      // the direct method that gets created for B.m during vertical class merging.
+      super.m();
+    }
+  }
+}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 01e35f3..1a20ce3 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -13,6 +13,9 @@
 -keep public class classmerging.ExceptionTest {
   public static void main(...);
 }
+-keep public class classmerging.RewritePinnedMethodTest {
+  public static void main(...);
+}
 -keep public class classmerging.SimpleInterfaceAccessTest {
   public static void main(...);
 }
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 9560c1f..60ebf39 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -4,28 +4,28 @@
 
 package com.android.tools.r8;
 
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
 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.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.android.tools.r8.utils.TestDescriptionWatcher;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.io.ByteStreams;
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -33,20 +33,17 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 
 public abstract class RunExamplesJava9Test
-      <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
+    <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
   static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_JAVA9_BUILD_DIR;
 
   abstract class TestRunner<C extends TestRunner<C>> {
@@ -99,10 +96,6 @@
       return self();
     }
 
-    C withMainDexClass(String... classes) {
-      return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
-    }
-
     C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
@@ -111,15 +104,19 @@
       return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
     }
 
+    void combinedOptionConsumer(InternalOptions options) {
+      for (Consumer<InternalOptions> consumer : optionConsumers) {
+        consumer.accept(options);
+      }
+    }
+
     C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
       builderTransformations.add(builderTransformation);
       return self();
     }
 
-    void combinedOptionConsumer(InternalOptions options) {
-      for (Consumer<InternalOptions> consumer : optionConsumers) {
-        consumer.accept(options);
-      }
+    C withMainDexClass(String... classes) {
+      return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
     }
 
     Path build() throws Throwable {
@@ -179,19 +176,19 @@
     ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
     builder
         .put(DexVm.Version.V4_0_4, ImmutableList.of(
-            "native-private-interface-methods",// Dex version not supported
+            "native-private-interface-methods", // Dex version not supported
             "varhandle"
         ))
         .put(DexVm.Version.V4_4_4, ImmutableList.of(
-            "native-private-interface-methods",// Dex version not supported
+            "native-private-interface-methods", // Dex version not supported
             "varhandle"
         ))
         .put(DexVm.Version.V5_1_1, ImmutableList.of(
-            "native-private-interface-methods",// Dex version not supported
+            "native-private-interface-methods", // Dex version not supported
             "varhandle"
         ))
-        .put(DexVm.Version.V6_0_1, ImmutableList.of("native-private-interface-methods",
-            // Dex version not supported
+        .put(DexVm.Version.V6_0_1, ImmutableList.of(
+            "native-private-interface-methods", // Dex version not supported
             "varhandle"
         ))
         .put(DexVm.Version.V7_0_0, ImmutableList.of(
@@ -208,12 +205,14 @@
   // Defines methods failing on JVM, specifies the output to be used for comparison.
   private static Map<String, String> expectedJvmResult =
       ImmutableMap.of(
-          "native-private-interface-methods", "0: s>i>a\n"
+          "native-private-interface-methods",
+          "0: s>i>a\n"
               + "1: d>i>s>i>a\n"
               + "2: l>i>s>i>a\n"
               + "3: x>s\n"
               + "4: c>d>i>s>i>a\n",
-          "desugared-private-interface-methods", "0: s>i>a\n"
+          "desugared-private-interface-methods",
+          "0: s>i>a\n"
               + "1: d>i>s>i>a\n"
               + "2: l>i>s>i>a\n"
               + "3: x>s\n"
@@ -245,21 +244,34 @@
 
   @Test
   public void nativePrivateInterfaceMethods() throws Throwable {
-    test("native-private-interface-methods", "privateinterfacemethods", "PrivateInterfaceMethods")
+    test("native-private-interface-methods",
+        "privateinterfacemethods", "PrivateInterfaceMethods")
         .withMinApiLevel(AndroidApiLevel.N.getLevel())
         .run();
   }
 
   @Test
   public void desugaredPrivateInterfaceMethods() throws Throwable {
-    test("desugared-private-interface-methods", "privateinterfacemethods",
-        "PrivateInterfaceMethods")
+    final String iName = "privateinterfacemethods.I";
+    test("desugared-private-interface-methods",
+        "privateinterfacemethods", "PrivateInterfaceMethods")
         .withMinApiLevel(AndroidApiLevel.M.getLevel())
+        .withDexCheck(dexInspector -> {
+          ClassSubject companion = dexInspector.clazz(
+              iName + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX);
+          assertThat(companion, isPresent());
+          MethodSubject iFoo = companion.method(
+              "java.lang.String",
+              InterfaceMethodRewriter.PRIVATE_METHOD_PREFIX + "iFoo",
+              ImmutableList.of(iName, "boolean"));
+          assertThat(iFoo, isPresent());
+          assertTrue(iFoo.getMethod().isPublicMethod());
+        })
         .run();
   }
 
   @Test
-  public void varHAndle() throws Throwable {
+  public void varHandle() throws Throwable {
     test("varhandle", "varhandle", "VarHandleTests")
         .withMinApiLevel(AndroidApiLevel.P.getLevel())
         .run();
@@ -285,7 +297,7 @@
       thrown.expect(Throwable.class);
     }
     String output = ToolHelper.runArtNoVerificationErrors(
-        Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+        Arrays.stream(dexes).map(Path::toString).collect(Collectors.toList()),
         qualifiedMainClass,
         null);
     String jvmResult = null;
@@ -308,17 +320,4 @@
     }
   }
 
-  protected DexInspector getMainDexInspector(Path zip)
-      throws ZipException, IOException, ExecutionException {
-    try (ZipFile zipFile = new ZipFile(zip.toFile(), StandardCharsets.UTF_8)) {
-      try (InputStream in =
-          zipFile.getInputStream(zipFile.getEntry(ToolHelper.DEFAULT_DEX_FILENAME))) {
-        return new DexInspector(
-            AndroidApp.builder()
-                .addDexProgramData(ByteStreams.toByteArray(in), Origin.unknown())
-                .build());
-      }
-    }
-  }
-
-}
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
index 6069b4c..da32cce 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
@@ -31,7 +31,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-
 @RunWith(Parameterized.class)
 public class MethodHandleTestRunner extends TestBase {
   static final Class<?> CLASS = MethodHandleTest.class;
@@ -169,7 +168,9 @@
           Arrays.asList(
               "-keep public class com.android.tools.r8.cf.MethodHandleTest {",
               "  public static void main(...);",
-              "}"),
+              "}",
+              // Disallow merging MethodHandleTest$I into MethodHandleTest$Impl
+              "-keep public interface com.android.tools.r8.cf.MethodHandleTest$I"),
           Origin.unknown());
     }
     try {
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 7016163..1e71a82 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -289,6 +289,32 @@
             "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"));
   }
 
+  // TODO(christofferqa): This test checks that the invoke-super instruction in B is not rewritten
+  // into an invoke-direct instruction after it gets merged into class C. We should add a test that
+  // checks that this works with and without inlining of the method B.m().
+  @Test
+  public void testRewritePinnedMethod() throws Exception {
+    String main = "classmerging.RewritePinnedMethodTest";
+    Path[] programFiles =
+        new Path[] {
+          CF_DIR.resolve("RewritePinnedMethodTest.class"),
+          CF_DIR.resolve("RewritePinnedMethodTest$A.class"),
+          CF_DIR.resolve("RewritePinnedMethodTest$B.class"),
+          CF_DIR.resolve("RewritePinnedMethodTest$C.class")
+        };
+    Set<String> preservedClassNames =
+        ImmutableSet.of(
+            "classmerging.RewritePinnedMethodTest",
+            "classmerging.RewritePinnedMethodTest$A",
+            "classmerging.RewritePinnedMethodTest$C");
+    runTest(
+        main,
+        programFiles,
+        preservedClassNames,
+        getProguardConfig(
+            EXAMPLE_KEEP, "-keep class classmerging.RewritePinnedMethodTest$A { *; }"));
+  }
+
   @Test
   public void testTemplateMethodPattern() throws Exception {
     String main = "classmerging.TemplateMethodTest";
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 5df1cee..38768cd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -4,9 +4,11 @@
 
 package com.android.tools.r8.ir.optimize.classinliner;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 import com.android.tools.r8.OutputMode;
@@ -32,10 +34,13 @@
 import com.android.tools.r8.ir.optimize.classinliner.trivial.Iface2Impl;
 import com.android.tools.r8.ir.optimize.classinliner.trivial.ReferencedFields;
 import com.android.tools.r8.ir.optimize.classinliner.trivial.TrivialTestClass;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import java.nio.file.Path;
 import java.util.Collections;
@@ -178,6 +183,39 @@
     assertFalse(inspector.clazz(BuildersTestClass.Pos.class).isPresent());
   }
 
+  @Test
+  public void testErroneousInput() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+
+    ClassBuilder testClass = builder.addClass("A");
+    testClass.addStaticFinalField("f", "I", "123");
+    testClass.addDefaultConstructor();
+
+    ClassBuilder mainClass = builder.addClass("Main");
+    mainClass.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  new A",
+        "  dup",
+        "  invokespecial A/<init>()V",
+        "  getfield A/f I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    AndroidApp compiled =
+        compileWithR8(builder.build(), getProguardConfig(mainClass.name), this::configure);
+
+    // Check that the code fails with an IncompatibleClassChangeError with Java.
+    ProcessResult javaResult =
+        runOnJavaRaw(mainClass.name, builder.buildClasses().toArray(new byte[2][]));
+    assertThat(javaResult.stderr, containsString("IncompatibleClassChangeError"));
+
+    // Check that the code fails with an IncompatibleClassChangeError with ART.
+    ProcessResult artResult = runOnArtRaw(compiled, mainClass.name);
+    assertThat(artResult.stderr, containsString("IncompatibleClassChangeError"));
+  }
+
   private Set<String> collectNewInstanceTypes(
       ClassSubject clazz, String methodName, String... params) {
     assertNotNull(clazz);
@@ -189,15 +227,8 @@
   }
 
   private AndroidApp runR8(AndroidApp app, Class mainClass) throws Exception {
-    String config = keepMainProguardConfiguration(mainClass) + "\n"
-        + "-dontobfuscate\n"
-        + "-allowaccessmodification";
-
-    AndroidApp compiled = compileWithR8(app, config,
-        o -> {
-          o.enableClassInlining = true;
-          o.classInliningInstructionLimit = 10000;
-        });
+    AndroidApp compiled =
+        compileWithR8(app, getProguardConfig(mainClass.getCanonicalName()), this::configure);
 
     // Materialize file for execution.
     Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
@@ -220,4 +251,16 @@
 
     return compiled;
   }
+
+  private String getProguardConfig(String main) {
+    return keepMainProguardConfiguration(main)
+        + "\n"
+        + "-dontobfuscate\n"
+        + "-allowaccessmodification";
+  }
+
+  private void configure(InternalOptions options) {
+    options.enableClassInlining = true;
+    options.classInliningInstructionLimit = 10000;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index 74e3d5e..6ebb08b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -83,13 +83,16 @@
           .addProperty("internalLateInitProp", JAVA_LANG_STRING, Visibility.INTERNAL)
           .addProperty("publicLateInitProp", JAVA_LANG_STRING, Visibility.PUBLIC);
 
-  private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
+  private Consumer<InternalOptions> disableClassInliningAndMerging = o -> {
+    o.enableClassInlining = false;
+    o.enableClassMerging = false;
+  };
 
   @Test
   public void testMutableProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_noUseOfProperties");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -112,7 +115,7 @@
   public void testMutableProperty_privateIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_usePrivateProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -134,7 +137,7 @@
   public void testMutableProperty_protectedIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_useProtectedProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -157,7 +160,7 @@
   public void testMutableProperty_internalIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_useInternalProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -180,7 +183,7 @@
   public void testMutableProperty_publicIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_usePublicProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -203,7 +206,7 @@
   public void testMutableProperty_primitivePropertyIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
         "mutableProperty_usePrimitiveProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           MUTABLE_PROPERTY_CLASS.getClassName());
@@ -228,7 +231,7 @@
   public void testLateInitProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
     String mainClass = addMainToClasspath("properties/LateInitPropertyKt",
         "lateInitProperty_noUseOfProperties");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
@@ -252,7 +255,7 @@
   public void testLateInitProperty_privateIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties/LateInitPropertyKt", "lateInitProperty_usePrivateLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
@@ -275,7 +278,7 @@
   public void testLateInitProperty_protectedIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath("properties/LateInitPropertyKt",
         "lateInitProperty_useProtectedLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
@@ -296,7 +299,7 @@
   public void testLateInitProperty_internalIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties/LateInitPropertyKt", "lateInitProperty_useInternalLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
@@ -315,7 +318,7 @@
   public void testLateInitProperty_publicIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties/LateInitPropertyKt", "lateInitProperty_usePublicLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           LATE_INIT_PROPERTY_CLASS.getClassName());
@@ -334,7 +337,7 @@
   public void testUserDefinedProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
     String mainClass = addMainToClasspath(
         "properties/UserDefinedPropertyKt", "userDefinedProperty_noUseOfProperties");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           USER_DEFINED_PROPERTY_CLASS.getClassName());
@@ -351,7 +354,7 @@
   public void testUserDefinedProperty_publicIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties/UserDefinedPropertyKt", "userDefinedProperty_useProperties");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject classSubject = checkClassIsKept(dexInspector,
           USER_DEFINED_PROPERTY_CLASS.getClassName());
@@ -377,7 +380,7 @@
   public void testCompanionProperty_primitivePropertyCannotBeInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties.CompanionPropertiesKt", "companionProperties_usePrimitiveProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
@@ -408,7 +411,7 @@
   public void testCompanionProperty_privatePropertyIsAlwaysInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
@@ -442,7 +445,7 @@
   public void testCompanionProperty_internalPropertyCannotBeInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
@@ -473,7 +476,7 @@
   public void testCompanionProperty_publicPropertyCannotBeInlined() throws Exception {
     String mainClass = addMainToClasspath(
         "properties.CompanionPropertiesKt", "companionProperties_usePublicProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector,
           "properties.CompanionProperties");
@@ -505,7 +508,7 @@
     final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
     String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
         "companionLateInitProperties_usePrivateLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
@@ -536,7 +539,7 @@
     final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
     String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
         "companionLateInitProperties_useInternalLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
@@ -560,7 +563,7 @@
     final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
     String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
         "companionLateInitProperties_usePublicLateInitProp");
-    runTest(PACKAGE_NAME, mainClass, disableClassInliner, (app) -> {
+    runTest(PACKAGE_NAME, mainClass, disableClassInliningAndMerging, (app) -> {
       DexInspector dexInspector = new DexInspector(app);
       ClassSubject outerClass = checkClassIsKept(dexInspector, testedClass.getOuterClassName());
       ClassSubject companionClass = checkClassIsKept(dexInspector, testedClass.getClassName());
diff --git a/third_party/r8.tar.gz.sha1 b/third_party/r8.tar.gz.sha1
index fbb2aa8..06f1922 100644
--- a/third_party/r8.tar.gz.sha1
+++ b/third_party/r8.tar.gz.sha1
@@ -1 +1 @@
-38b85dcea75f12c37332a5425c87733e78754ba6
\ No newline at end of file
+ad3858bd8a8597f674d1af6bb762c16672dc6436
\ No newline at end of file
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 8f440dd..8b086e5 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -19,9 +19,10 @@
 import re
 import sys
 import time
+import zipfile
+
 import toolhelper
 import utils
-import zipfile
 
 KEEP = '-keep public class %s { public static void main(...); }\n'
 MANIFEST_PATH = 'META-INF/MANIFEST.MF'
@@ -85,7 +86,8 @@
     return mo.group(1)
 
 def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
-                lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None):
+                lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None,
+                track_memory_file=None):
   if output_jar is None:
     output_jar = generate_output_name(input_jar, mainclass)
   with utils.TempDir() as path:
@@ -105,10 +107,15 @@
             '--release',
             tmp_input_path)
     start_time = time.time()
-    return_code = toolhelper.run('r8', args, debug=debug, build=build)
+    return_code = toolhelper.run('r8', args, debug=debug, build=build,
+                                 track_memory_file=track_memory_file)
     if benchmark_name:
       elapsed_ms = 1000 * (time.time() - start_time)
       print('%s(RunTimeRaw): %s ms' % (benchmark_name, elapsed_ms))
+      if track_memory_file:
+        print('%s(MemoryUse): %s' %
+              (benchmark_name, utils.grep_memoryuse(track_memory_file)))
+
     return return_code
 
 if __name__ == '__main__':
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
index 2ae97b2..8230c71 100755
--- a/tools/run_bootstrap_benchmark.py
+++ b/tools/run_bootstrap_benchmark.py
@@ -3,21 +3,53 @@
 # 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 argparse
-import minify_tool
 import os
 import sys
+
+import minify_tool
+import toolhelper
 import utils
 
-
 PINNED_R8_JAR = os.path.join(utils.REPO_ROOT, 'third_party/r8/r8.jar')
+PINNED_PGR8_JAR = os.path.join(utils.REPO_ROOT, 'third_party/r8/r8-pg6.0.1.jar')
 
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    '--name', metavar='NAME', dest='benchmark_name',
-    help='Print "<NAME>(RunTimeRaw): <elapsed> ms" at the end')
-
+def dex(input, output):
+  return_code = toolhelper.run(
+      'd8', [
+        input,
+        '--output', output,
+        '--lib', utils.RT_JAR,
+        '--min-api', '10000',
+        '--no-desugaring',
+      ],
+      debug=False,
+      build=False)
+  if return_code != 0:
+    sys.exit(return_code)
 
 if __name__ == '__main__':
-  sys.exit(minify_tool.minify_tool(input_jar=PINNED_R8_JAR, debug=False,
-                                   build=False, **vars(parser.parse_args())))
+  with utils.TempDir() as temp:
+    memory_file = os.path.join(temp, 'memory.dump')
+    r8_output = os.path.join(temp, 'r8.zip')
+    d8_r8_output = os.path.join(temp, 'd8r8.zip')
+    d8_pg_output = os.path.join(temp, 'd8pg.zip')
+
+    return_code = minify_tool.minify_tool(
+      input_jar=PINNED_R8_JAR,
+      output_jar=r8_output,
+      debug=False,
+      build=False,
+      track_memory_file=memory_file,
+      benchmark_name="BootstrapR8")
+    if return_code != 0:
+      sys.exit(return_code)
+
+    dex(r8_output, d8_r8_output)
+    print "BootstrapR8(CodeSize):", os.path.getsize(r8_output)
+    print "BootstrapR8Dex(CodeSize):", os.path.getsize(d8_r8_output)
+
+    dex(PINNED_PGR8_JAR, d8_pg_output)
+    print "BootstrapPG(CodeSize):", os.path.getsize(PINNED_PGR8_JAR)
+    print "BootstrapPGDex(CodeSize):", os.path.getsize(d8_pg_output)
+
+  sys.exit(0)
\ No newline at end of file